feat(wxread): align pagination, rendering, and docs
This commit is contained in:
+16
-3
@@ -16,7 +16,7 @@ RDReaderView 是一个 iOS 阅读器组件库(CocoaPods),提供开箱即
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Demo / 宿主 App │
|
||||
│ ViewController → RDReaderController(demo 级路由控制器) │
|
||||
│ ViewController → RDURLReaderController(demo 级路由控制器)│
|
||||
└───────────────────────────┬─────────────────────────────┘
|
||||
│
|
||||
┌───────────────────────────▼─────────────────────────────┐
|
||||
@@ -62,14 +62,27 @@ RDReaderView 是一个 iOS 阅读器组件库(CocoaPods),提供开箱即
|
||||
│ RDEPUBStyleSheetBuilder / RDEPUBJavaScriptBridge │
|
||||
│ RDEPUBFixedLayoutTemplate / RDEPUBAssetRepository │
|
||||
│ RDEPUBRenderRequest │
|
||||
│ │
|
||||
│ Search 层 │
|
||||
│ RDEPUBSearchEngine(协议)/ RDEPUBHTMLSearchEngine │
|
||||
│ RDEPUBSearchModels(SearchMatch/Result/State/Presentation)│
|
||||
└───────────────────────────┬─────────────────────────────┘
|
||||
│
|
||||
|
||||
┌───────────────────────────▼─────────────────────────────┐
|
||||
│ EPUBTextRendering 层(文本 EPUB 渲染) │
|
||||
│ RDEPUBTextRenderer(协议) │
|
||||
│ RDEPUBDTCoreTextRenderer(DTCoreText 实现) │
|
||||
│ RDEPUBTextRendererSupport / RDEPUBTextPaginationSupport │
|
||||
│ RDEPUBTextBookBuilder │
|
||||
│ RDEPUBTextBookBuilder / RDPlainTextBookBuilder │
|
||||
│ RDEPUBTextLayouter / RDEPUBTextLayoutFrame │
|
||||
│ RDEPUBTextSearchEngine │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ LegacyRDReaderController/(旧版阅读器,保留兼容) │
|
||||
│ RDReaderController(旧版)/ RDReaderManager │
|
||||
│ RDReaderContentView / RDReaderEPUBContentView │
|
||||
│ SSChapterListController / SSEventTrigger │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
### 2.1 主控制器 `RDEPUBReaderController`
|
||||
|
||||
- 文件:`EPUBUI/RDEPUBReaderController.swift`(~1255 行)
|
||||
- 入口方法:`init(epubURL:configuration:persistence:)`
|
||||
- 入口方法:
|
||||
- `init(epubURL:configuration:persistence:)` — 标准 EPUB 阅读入口
|
||||
- `init(textBook:bookIdentifier:title:textFileURL:configuration:)` — 纯文本书籍阅读入口(由 `RDPlainTextBookBuilder` 构建 `RDEPUBTextBook` 后传入)
|
||||
- 职责:
|
||||
- 加载 EPUB:后台解析 → 应用分页 → 恢复阅读位置
|
||||
- 实现 `RDReaderDataSource` / `RDReaderDelegate` 为容器提供数据
|
||||
@@ -38,17 +40,19 @@
|
||||
### 2.3 委托协议 `RDEPUBReaderDelegate`
|
||||
|
||||
- 文件:`EPUBUI/RDEPUBReaderDelegate.swift`
|
||||
- 10 个可选方法(全部有默认空实现):
|
||||
- 12 个可选方法(全部有默认空实现):
|
||||
- `didOpen`:成功打开出版物
|
||||
- `didUpdateLocation`:翻页或滚动时位置更新
|
||||
- `didReachEnd`:到达最后一页
|
||||
- `didChangeSelection`:文本选择变化或清除
|
||||
- `didUpdateHighlights`:高亮增删改
|
||||
- `didUpdateBookmarks`:书签增删改
|
||||
- `didUpdateSearchResult`:搜索状态变化
|
||||
- `didChangeCurrentSearchMatch`:当前搜索匹配项变化
|
||||
- `didUpdateCurrentTableOfContentsItem`:翻页时匹配的目录项
|
||||
- `didActivateExternalLink`:外部链接点击
|
||||
- `didFailWithError`:解析或分页错误
|
||||
- `configureTopToolView`:自定义顶部工具栏
|
||||
|
||||
### 2.4 持久化 `RDEPUBReaderPersistence`
|
||||
|
||||
@@ -263,7 +267,7 @@ RDEPUBReaderTableOfContentsItem
|
||||
|
||||
### 外部通信(Delegate)
|
||||
|
||||
- `RDEPUBReaderDelegate`:11 个可选方法,覆盖打开、位置更新、到达末尾、选择变化、高亮更新、书签更新、搜索更新、目录项更新、外部链接、错误
|
||||
- `RDEPUBReaderDelegate`:12 个可选方法,覆盖打开、位置更新、到达末尾、选择变化、高亮更新、书签更新、搜索更新、目录项更新、外部链接、错误、工具栏自定义
|
||||
|
||||
### 内部通信(闭包)
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
| `RDEPUBResourceURLSchemeHandler.swift` | `ss-reader://` 协议,从本地解压目录提供所有 EPUB 资源 |
|
||||
| `RDEPUBStyleSheetBuilder.swift` | 生成注入 WebView 的 CSS |
|
||||
| `RDEPUBJavaScriptBridge.swift` | JS ↔ Swift 消息定义和编解码 |
|
||||
| `RDEPUBSearchEngine.swift` | 搜索协议定义 + WebView 全文搜索引擎 |
|
||||
| `RDEPUBSearchModels.swift` | 搜索模型(SearchMatch/Result/State/Presentation) |
|
||||
|
||||
### 2.2 EPUBTextRendering 层
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Reflowable EPUB 使用 WXRead 风格原生渲染:详细设计
|
||||
|
||||
> 文档目的:讨论并固化“将 ReadViewSDK 的 reflowable EPUB 渲染/排版/分页改为参考 Doc/WXRead 的微信读书(WXRead)原生渲染方式”的可落地设计,供后续开发与回归使用。
|
||||
> 文档目的:讨论并固化“将 ReadViewSDK 的 reflowable EPUB 渲染/排版/分页改为参考 Doc/WXRead 的读书(WXRead)原生渲染方式”的可落地设计,供后续开发与回归使用。
|
||||
> 版本:v0(设计草案)
|
||||
> 日期:2026-05-21
|
||||
|
||||
@@ -79,7 +79,7 @@ ReadViewSDK 当前对 EPUB 有三类渲染路径:
|
||||
|
||||
## 4. 是否能直接使用 Doc/WXRead 中的 JS/CSS?
|
||||
|
||||
结论:**不建议、也不应该直接把“来自微信读书 App bundle 的私有 JS/CSS”拷贝进 SDK 作为产品代码**;但可以按以下原则“选择性使用”:
|
||||
结论:**不建议、也不应该直接把“来自读书 App bundle 的私有 JS/CSS”拷贝进 SDK 作为产品代码**;但可以按以下原则“选择性使用”:
|
||||
|
||||
### 4.1 可以使用的情况(需满足其一)
|
||||
|
||||
@@ -91,7 +91,7 @@ ReadViewSDK 当前对 EPUB 有三类渲染路径:
|
||||
|
||||
### 4.2 不建议/不能直接使用的情况
|
||||
|
||||
- 无明确许可证头、看起来是微信读书私有逻辑/样式(例如 `weread-highlighter.js`、`MediaPlatform.js`、`replace.css` 等):默认视为私有作品,不应直接拷贝使用。
|
||||
- 无明确许可证头、看起来是读书私有逻辑/样式(例如 `weread-highlighter.js`、`MediaPlatform.js`、`replace.css` 等):默认视为私有作品,不应直接拷贝使用。
|
||||
- 即便是“Safari 默认样式”类文件(例如 `default.css` 的注释提到 Safari),也不建议直接照搬;我们可以根据需求写一份“SDK 自己的 default.css / replace.css”,只实现必要规则。
|
||||
|
||||
### 4.3 对本次需求的实际影响
|
||||
|
||||
@@ -59,8 +59,9 @@
|
||||
- 文件:`Sources/RDReaderView/RDURLReaderController.swift`(~105 行)
|
||||
- 职责:
|
||||
- 最简入口:传入 URL 即可打开书籍
|
||||
- `.epub` 扩展名 → 创建 `RDEPUBReaderController`
|
||||
- 其他 → 创建 `RDPlainTextReaderController`(UITextView 只读展示,尝试 UTF-8 → GBK → GB2312 解码)
|
||||
- `.epub` 扩展名 → 创建 `RDEPUBReaderController(epubURL:configuration:)`
|
||||
- 其他扩展名 → 使用 `RDPlainTextBookBuilder` 构建 `RDEPUBTextBook`,然后创建 `RDEPUBReaderController(textBook:bookIdentifier:title:textFileURL:configuration:)`
|
||||
- 分页失败时回退到纯 `UITextView` 展示(尝试 UTF-8 → GBK → GB2312 解码)
|
||||
- 导航栏标题设为文件名(去掉扩展名)
|
||||
|
||||
## 3. 主流程(代码级)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 微信读书 EPUB 阅读器 — 核心架构与数据流图
|
||||
# 读书 EPUB 阅读器 — 核心架构与数据流图
|
||||
|
||||
> 逆向工程目标: WeRead v10.0.3 (Build 79), arm64, 63MB 主二进制
|
||||
> 目标: WeRead v10.0.3 (Build 79)
|
||||
|
||||
---
|
||||
|
||||
@@ -10,12 +10,11 @@
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ WeRead.app (iOS/macOS Catalyst) │
|
||||
│ 主二进制: 63MB, Mach-O arm64 │
|
||||
│ WeRead.app (iOS/macOS Catalyst) │ │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────────────┐ │
|
||||
│ │ 主二进制 (WeRead) │ │
|
||||
│ │ WeRead │ │
|
||||
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ │
|
||||
│ │ │ 阅读器模块 │ │ 书架模块 │ │ 其他业务模块 │ │ │
|
||||
│ │ │ WRReader* │ │ WRBookshelf* │ │ WRDiscover/WRMarket │ │ │
|
||||
@@ -216,7 +215,7 @@ WRReaderViewController (431 methods, 阅读器主控制器)
|
||||
|
||||
```
|
||||
┌──────────┐ HTTPS ┌──────────────────────────────────────────────────┐
|
||||
│ 微信读书 │─────────────→│ ① WRBookNetwork.loadTarForEpubBookId:chapter: │
|
||||
│ 读书 │─────────────→│ ① WRBookNetwork.loadTarForEpubBookId:chapter: │
|
||||
│ 服务器 │ 加密 ZIP │ isPreload: │
|
||||
│ │ {bookId}_ │ ↓ │
|
||||
│ │ DECRYPT.zip │ ② WRBookNetwork.handleUnzipWithBookId: │
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 微信读书 EPUB 阅读器 — 符号恢复与核心算法
|
||||
# 读书 EPUB 阅读器 — 符号恢复与核心算法
|
||||
|
||||
> 基于 WeRead v10.0.3 (Build 79) 二进制逆向
|
||||
> 方法签名来源: __objc_methname 段 + binary strings
|
||||
@@ -254,7 +254,7 @@ def cascade_stylesheets(default_css: str, replace_css: str, dark_css: str,
|
||||
|
||||
Args:
|
||||
default_css: Safari 默认样式 (default.css)
|
||||
replace_css: 微信读书默认替换 (replace.css)
|
||||
replace_css: 读书默认替换 (replace.css)
|
||||
dark_css: 暗黑主题 (dark.css)
|
||||
epub_css: EPUB 书籍内嵌 CSS
|
||||
user_settings_css: 用户自定义设置 (字号/行高/主题)
|
||||
@@ -295,7 +295,7 @@ def cascade_stylesheets(default_css: str, replace_css: str, dark_css: str,
|
||||
|
||||
# 按优先级逐层合并
|
||||
result = parse_css(default_css) # 层1: 基础
|
||||
result = merge(result, parse_css(replace_css)) # 层2: 微信读书默认
|
||||
result = merge(result, parse_css(replace_css)) # 层2: 读书默认
|
||||
result = merge(result, parse_css(dark_css)) # 层3: 暗黑主题
|
||||
result = merge(result, parse_css(epub_css)) # 层4: EPUB 内嵌
|
||||
result = merge(result, parse_css(user_settings_css)) # 层5: 用户设置 (最高优先级)
|
||||
@@ -630,7 +630,7 @@ def convert_hans_to_hant(attributed_string: str) -> str:
|
||||
# OpenCC: opencc_convert("s2t", text)
|
||||
|
||||
# 简化实现: 使用 Unicode 码点映射表
|
||||
# 微信读书内部可能使用腾讯自研的繁简转换库
|
||||
# 读书内部可能使用腾讯自研的繁简转换库
|
||||
conversion_table = load_hans_to_hant_table() # ~8000 个映射
|
||||
|
||||
result = []
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
# 微信读书 EPUB 阅读器 — 数据结构与 API 协议定义
|
||||
|
||||
> 基于 WeRead v10.0.3 (Build 79) 逆向工程
|
||||
> 数据结构来源: __objc_methtype + class_ro_t ivar 段
|
||||
> API 来源: WRBookNetwork 44 个 class method 签名
|
||||
# 读书 EPUB 阅读器 — 数据结构与 API 协议定义
|
||||
|
||||
> 基于 WeRead v10.0.3 (Build 79)
|
||||
---
|
||||
|
||||
## 一、关键数据结构定义 (Header Files)
|
||||
@@ -129,7 +126,7 @@ typedef NS_ENUM(NSInteger, WRChapterFormat) {
|
||||
// 表格
|
||||
@property (nonatomic, strong) DTTableStyle *tableStyle; // 表格样式
|
||||
|
||||
// 微信读书自定义属性
|
||||
// 读书自定义属性
|
||||
@property (nonatomic, assign) NSInteger verticalCenterStyle; // wr-vertical-center-style
|
||||
@property (nonatomic, assign) BOOL pageRelate; // weread-page-relate
|
||||
@property (nonatomic, assign) BOOL avoidPageBreakInside; // 断页保护
|
||||
@@ -185,7 +182,7 @@ typedef NS_ENUM(NSInteger, WRChapterFormat) {
|
||||
@property (nonatomic, assign) CGFloat maximumLineHeight; // 最大行高
|
||||
@property (nonatomic, assign) CGFloat lineHeightMultiple; // 行高倍数
|
||||
|
||||
// 微信读书扩展
|
||||
// 读书扩展
|
||||
@property (nonatomic, assign) CGFloat defaultTabInterval; // 默认制表位
|
||||
|
||||
@end
|
||||
@@ -809,7 +806,7 @@ NSString *const kWRUserVidKey = @"com.weread.user.vid";
|
||||
### 3.4 NSAttributedString 自定义属性键
|
||||
|
||||
```objc
|
||||
// 微信读书在 NSAttributedString 中注入的自定义属性
|
||||
// 读书在 NSAttributedString 中注入的自定义属性
|
||||
// 用于在排版结果中传递页面布局元数据
|
||||
|
||||
NSString *const DTPageBackgroundColorAttribute = @"DTPageBackgroundColor";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# CSS/JS/字体资源分析
|
||||
|
||||
本文档对微信读书 EPUB 阅读器的前端资源文件进行全面分析,涵盖 CSS 样式表、JavaScript 脚本和字体文件。
|
||||
本文档对读书 EPUB 阅读器的前端资源文件进行全面分析,涵盖 CSS 样式表、JavaScript 脚本和字体文件。
|
||||
|
||||
---
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
### 1.1 样式层叠架构
|
||||
|
||||
微信读书的 CSS 采用分层架构:
|
||||
读书的 CSS 采用分层架构:
|
||||
|
||||
```
|
||||
default.css (基础HTML样式)
|
||||
@@ -46,7 +46,7 @@ Safari 派生的基础 HTML 默认样式,为 WebView 渲染提供标准化起
|
||||
|
||||
**路径**: `output/resources/css/replace.css` (172行)
|
||||
|
||||
EPUB 内容的核心替换样式,定义了微信读书特有的排版规则。
|
||||
EPUB 内容的核心替换样式,定义了读书特有的排版规则。
|
||||
|
||||
**字体设置**:
|
||||
- 标题和版权页使用 `"SourceHanSerifCN-Medium"` 字体
|
||||
@@ -62,7 +62,7 @@ EPUB 内容的核心替换样式,定义了微信读书特有的排版规则。
|
||||
| `.fifthTitle` | 1.05em | normal |
|
||||
| `.sixthTitle` | 1em | normal |
|
||||
|
||||
**微信读书自定义CSS属性**:
|
||||
**读书自定义CSS属性**:
|
||||
|
||||
```css
|
||||
/* 垂直居中样式 - 值2用于正文图片 */
|
||||
@@ -545,7 +545,7 @@ getAllForwardLinks()
|
||||
// 转换H5链接为原生数据属性
|
||||
transferH5Link()
|
||||
|
||||
// 解密微信读书加密链接
|
||||
// 解密读书加密链接
|
||||
WRLink_decrypt(str)
|
||||
// type '3' = 数字
|
||||
// 其他 = 十六进制编码文本
|
||||
@@ -573,7 +573,7 @@ document.addEventListener("selectionchange", debounce(function(e) {
|
||||
|
||||
#### 2.4.1 rangy-highlighter.js (711行)
|
||||
|
||||
Rangy 高亮器模块,带微信读书自定义修改。
|
||||
Rangy 高亮器模块,带读书自定义修改。
|
||||
|
||||
**CharacterRange 类**:
|
||||
```javascript
|
||||
@@ -1099,7 +1099,7 @@ line-height: 30px;
|
||||
|
||||
## 四、关键发现与实现细节
|
||||
|
||||
### 4.1 微信读书自定义CSS属性
|
||||
### 4.1 读书自定义CSS属性
|
||||
|
||||
**wr-vertical-center-style**:
|
||||
- 值 `1`:`.wr-vertical-center` 类,强制垂直居中
|
||||
@@ -1137,7 +1137,7 @@ resultIframe.src = 'wereadapijs://private/setresult/' + scene + '&' + encodedDat
|
||||
2. `rangy-textrange.js` - 字符级范围操作(支持图片文本)
|
||||
3. `rangy-classapplier.js` - CSS类应用
|
||||
4. `rangy-highlighter.js` - 高亮管理(序列化/反序列化)
|
||||
5. `weread-highlighter.js` - 微信读书业务逻辑
|
||||
5. `weread-highlighter.js` - 读书业务逻辑
|
||||
|
||||
**标注持久化**:
|
||||
```javascript
|
||||
@@ -1278,13 +1278,13 @@ RE.insertBook = function(id, title, author, cover) {
|
||||
|
||||
## 六、总结
|
||||
|
||||
微信读书的前端资源架构体现了以下设计特点:
|
||||
读书的前端资源架构体现了以下设计特点:
|
||||
|
||||
1. **分层样式系统**:通过 default.css → replace.css/MediaPlatform.css → dark.css 的层叠,实现内容类型和主题的灵活切换
|
||||
|
||||
2. **原生桥接机制**:WeReadApi.js 通过 iframe URL scheme 实现 JS 与原生的高效通信,支持异步回调
|
||||
|
||||
3. **扩展的Rangy库**:在标准 Rangy 基础上添加了图片文本支持、高亮合并、序列化等微信读书特有功能
|
||||
3. **扩展的Rangy库**:在标准 Rangy 基础上添加了图片文本支持、高亮合并、序列化等读书特有功能
|
||||
|
||||
4. **完整的标注系统**:支持高亮、想法、好友想法、引用、TTS五种标注类型,每种有独立的样式和交互
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# DTCoreText 自定义修改分析
|
||||
|
||||
微信读书 (WeRead) 基于开源 DTCoreText 库进行了深度定制,将其从一个通用的 HTML-to-NSAttributedString 转换库改造为一套完整的电子书分页渲染引擎。本文档详细记录所有自定义修改。
|
||||
读书 (WeRead) 基于开源 DTCoreText 库进行了深度定制,将其从一个通用的 HTML-to-NSAttributedString 转换库改造为一套完整的电子书分页渲染引擎。本文档详细记录所有自定义修改。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# EPUB 渲染管线详解
|
||||
|
||||
微信读书 (WeRead) 的 EPUB 渲染管线将原始 XHTML 文件转换为可分页、可交互的阅读视图。本文档完整描述从 EPUB 文件到屏幕像素的每一步。
|
||||
读书 (WeRead) 的 EPUB 渲染管线将原始 XHTML 文件转换为可分页、可交互的阅读视图。本文档完整描述从 EPUB 文件到屏幕像素的每一步。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,334 @@
|
||||
# WXRead/decompiled 逆向代码说明
|
||||
|
||||
## 概述
|
||||
|
||||
此目录包含从读书 (WeRead) iOS 客户端二进制逆向还原的头文件和实现代码。共 44 个文件(22 对 .h/.m),分为三大模块:
|
||||
|
||||
1. **阅读器核心** — 阅读控制器、页面渲染、排版引擎、EPUB 解析
|
||||
2. **标注系统** — 划线、高亮、书签、想法、Pencil 手写笔记
|
||||
3. **DTCoreText 定制** — 基于开源 DTCoreText 库的读书私有定制
|
||||
|
||||
---
|
||||
|
||||
## 架构总览
|
||||
|
||||
```
|
||||
WRReaderViewController (阅读器主控制器)
|
||||
├── WRPageViewController (翻页控制器, 支持 curl/slide/fade/none)
|
||||
│ └── WRPageView (页面渲染视图, CoreText 直接绘制)
|
||||
│ └── WRCoreTextLayoutFrame (单页排版帧)
|
||||
│ └── WRCoreTextLayoutLine (单行排版)
|
||||
├── WREpubParser (EPUB 文件解析)
|
||||
├── WREpubTypesetter (XHTML → NSAttributedString 排版)
|
||||
│ └── DTHTMLAttributedStringBuilder (HTML DOM → 属性字符串)
|
||||
│ └── DTHTMLElement (DOM 节点)
|
||||
├── WRChapterData (章节数据模型)
|
||||
├── WRChapterPageCount (分页计算器)
|
||||
├── WRCoreTextLayouter (CoreText 排版器)
|
||||
└── WRBookmark / WRPageHighlight / WRPageMark / WRPageUnderline (标注模型)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 阅读器核心模块
|
||||
|
||||
### `WRReaderViewController`
|
||||
|
||||
阅读器的主入口控制器,431 个方法,管理阅读状态的完整生命周期。
|
||||
|
||||
**核心职责:**
|
||||
- 管理 `WRReadingProgress`(阅读进度:章节索引、页码、字符偏移、滚动位置、阅读百分比)
|
||||
- 章节导航:`gotoChapterIdx:position:positionOfFile:`、`jumpReadingToNextChapter`、`jumpReadingToPreChapter`
|
||||
- 页面渲染:`renderPageView:progressData:source:` — 获取/计算章节数据、分页、创建/更新 WRPageView
|
||||
- 排版重排:`recomposeCurrentPageViewWithSource:` — 字号/行距/主题变更后触发
|
||||
- 进度持久化:`_saveReadingProgressAndIsAsync:`
|
||||
- 支持涂鸦模式 (doodle mode) 和自动阅读 (auto-read)
|
||||
- 通过 `WRReaderViewControllerDelegate` 回调通知外部进度变化和退出
|
||||
|
||||
### `WRPageViewController`
|
||||
|
||||
自定义的翻页控制器,支持四种翻页动画:
|
||||
|
||||
| 枚举值 | 样式 | 说明 |
|
||||
|--------|------|------|
|
||||
| `WRPageFlippingStyleCurl` | 纸张翻页 | 基于 UIPageViewController 的 curl 效果 |
|
||||
| `WRPageFlippingStyleSlide` | 水平滑动 | UIScrollView 驱动的滑动翻页 |
|
||||
| `WRPageFlippingStyleFade` | 淡入淡出 | 交叉渐变 |
|
||||
| `WRPageFlippingStyleNone` | 无动画 | 瞬间切换 |
|
||||
|
||||
**关键特性:**
|
||||
- 内部封装 `UIPageViewController`,提供 `WRPageViewControllerDelegate` / `WRPageViewControllerDataSource` 协议
|
||||
- 包含 3 个故障修补方法,处理 Apple `UIPageViewController` 的已知崩溃:
|
||||
- `detectNavigationDirectionCrashWithPageViewController:` — 检测导航方向崩溃
|
||||
- `patchNavigationDirectionFault` — 修复导航方向状态不一致
|
||||
- `patchNoViewControllerManagingPageViewFault` — 修复子 VC 丢失
|
||||
- `patchUIPageCurlFault` — 修复快速翻页时的动画故障
|
||||
|
||||
### `WRPageView`
|
||||
|
||||
单页渲染视图,继承 `UIView`,通过 CoreText 的 `drawRect:` 直接绘制文本,不使用 `UILabel` 或 `UITextView`。
|
||||
|
||||
**核心绘制:**
|
||||
- `drawInContext:withData:size:inRect:position:` — 将文本和行内图片直接绘制到 CGContext
|
||||
- 通过 `CTLineGetStringIndexForPosition` 进行 CoreText 级别的坐标点击测试
|
||||
|
||||
**UI 覆盖层:**
|
||||
- 章节标题头 (headerLabel)、背景图片 (backgroundImageView)
|
||||
- 加载指示器 (activityIndicator)、加载进度条 (loadingProgressView)
|
||||
- 状态提示 (statusLabel)、重试按钮 (retryButton)
|
||||
- 分享按钮、书签按钮、字号调节按钮
|
||||
- 好友想法按钮 (friendReviewsButton)
|
||||
|
||||
**文本选区:**
|
||||
- `stringIndexForPoint:` / `lineRangeForStringIndex:` — CoreText 坐标与字符索引转换
|
||||
- `isSelecting` / `selectedRange` — 选区状态
|
||||
- `clearSelection` — 清除选区
|
||||
|
||||
---
|
||||
|
||||
## EPUB 解析与排版
|
||||
|
||||
### `WREpubParser`
|
||||
|
||||
EPUB 文件解析器,处理完整的 EPUB 结构:
|
||||
|
||||
**解析流程:** `container.xml` → `content.opf` → `spine` → 章节文件
|
||||
|
||||
**主要 API:**
|
||||
- `initWithFilePath:book:` — 初始化
|
||||
- `parse:` — 执行解析,返回 YES/NO
|
||||
- `parseContainerXML:` — 解析 container.xml 获取 OPF 路径
|
||||
- `parseOPFAtRelativePath:error:` — 解析 content.opf,填充章节列表和资源映射
|
||||
- `parseNCX:` — 解析 toc.ncx 目录树
|
||||
- `contentForChapterAtIndex:error:` — 读取单章 XHTML 内容
|
||||
- `absolutePathForResource:` — 将相对资源路径解析为绝对路径
|
||||
|
||||
**错误码:**
|
||||
|
||||
| 错误码 | 含义 |
|
||||
|--------|------|
|
||||
| -1000 | 文件未找到 |
|
||||
| -1001 | container.xml 解析失败 |
|
||||
| -1002 | OPF 解析失败 |
|
||||
| -1003 | NCX 解析失败 |
|
||||
| -1004 | spine 为空 |
|
||||
| -1005 | 章节加载失败 |
|
||||
| -1006 | 解密失败 |
|
||||
|
||||
### `WREpubTypesetter`
|
||||
|
||||
EPUB 排版器,XHTML → `NSAttributedString` 的转换核心。全部为类方法,无需实例化。
|
||||
|
||||
**主入口方法:**
|
||||
```objc
|
||||
+ (NSAttributedString *)attributeStringWithFilePath:priority:
|
||||
insertArticleToolAttachment:insertBookChapterToolAttachment:
|
||||
insertRecommendView:book:chapter:pageFlippingStyle:
|
||||
renderErrorReason:isStyleFileNotFound:options:
|
||||
```
|
||||
|
||||
**处理流程:**
|
||||
1. 加载并级联合并 CSS(用户设置 > EPUB 内嵌 > 默认样式)
|
||||
2. 将 XHTML + 合并后的 CSS 喂入 `DTHTMLAttributedStringBuilder`
|
||||
3. 遍历生成的元素树,修补图片、链接、自定义属性
|
||||
4. 应用用户排版偏好(字体、行距、主题)
|
||||
5. 可选截断用于免费试读预览
|
||||
|
||||
**自定义 CSS 属性:**
|
||||
- `WREpubTypesetterVerticalCenterStyleAttribute` — 行内元素垂直居中(`wr-vertical-center-style`)
|
||||
- `WREpubTypesetterPageRelateAttribute` — 跨页关联标记(`weread-page-relate`)
|
||||
|
||||
**翻译渲染错误上报:**
|
||||
- `tryReportTranslationError:` — 双语(原文 + 翻译)渲染异常时上报分析
|
||||
|
||||
### `WREpubPositionConverter`
|
||||
|
||||
文件位置与字符位置的双向转换器。用于书签同步和阅读进度追踪。
|
||||
|
||||
**核心概念:** 将 `(fileIndex, row, column)` 三元组映射为全局字符偏移量。
|
||||
|
||||
**主要 API:**
|
||||
- `initWithFilePaths:attributedStrings:offset:isContainIntroFlyleaf:` — 初始化
|
||||
- `initIndices` — 构建内部索引表(调用前不能执行转换)
|
||||
- `indicesInFile:forRowColumnPairs:stringIndices:` — 行/列 → 字符索引
|
||||
- `stringRangeFromFileRange:` — 文件内范围 → 全局字符串范围
|
||||
- `totalCharacterCount` — 全书总字符数
|
||||
- `fileIndexForCharacterPosition:` — 全局位置 → 文件索引
|
||||
- `localOffsetInFileAtIndex:forGlobalPosition:` — 全局位置 → 文件内偏移
|
||||
|
||||
---
|
||||
|
||||
## 分页与布局
|
||||
|
||||
### `WRChapterPageCount`
|
||||
|
||||
章节分页计算器。管理每页的 `NSRange`。
|
||||
|
||||
**关键属性:**
|
||||
- `pageRanges` — 每页的字符范围数组 (NSValue-wrapped NSRange)
|
||||
- `totalPages` — 总页数
|
||||
|
||||
**关键方法:**
|
||||
- `currentCacheKeyWithBookId:` — 生成缓存键(编码书 ID + 当前排版设置,设置变更则失效)
|
||||
- `rangeValueWithPageInfo:` — 从服务端分页数据提取范围
|
||||
- `rangeForPageAtIndex:` / `pageIndexForCharacterIndex:` — 页码 ↔ 字符位置
|
||||
- `recalculatePageRangesForAttributedString:drawingSize:margins:` — 重算分页
|
||||
|
||||
### `WRCoreTextLayouter`
|
||||
|
||||
读书封装的 CoreText 排版器,创建 `CTTypesetter` / `CTFramesetter` 并产出 `WRCoreTextLayoutFrame` 页面对象。
|
||||
|
||||
**初始化:**
|
||||
- `initWithAttributedString:` / `initWithAttributedString:config:`
|
||||
- `WRCoreTextLayoutConfig` 配置:frame 宽高、内边距、栏数、栏间距、孤行/寡行控制、连字符
|
||||
|
||||
**排版 API:**
|
||||
- `createTypesetter` / `createFramesetter` / `invalidateTypesetter`
|
||||
- `layoutFrameWithRange:frame:` — 单帧排版
|
||||
- `layoutFramesForPageSize:` — 全文分页排版
|
||||
- `numberOfPagesForPageSize:` / `rangeForPageAtIndex:pageSize:` — 分页查询
|
||||
- `pageBackgroundImageAtRange:themeBgColor:` — 页面背景图
|
||||
- `resizedImageForImagePath:` — 图片缩放适配
|
||||
|
||||
### `WRCoreTextLayoutFrame`
|
||||
|
||||
单页排版帧,封装 `CTFrame`,管理 `WRCoreTextLayoutLine` 行数组。
|
||||
|
||||
**核心功能:**
|
||||
- 文本绘制:`drawInContext:image:size:inRect:position:`(含 CTFrameDraw + 图片附件 + 装饰元素)
|
||||
- 分栏支持:`numberOfColumns` / `columnGap`
|
||||
- 跨页避让:`avoidPageBreakInsideByRemovingLastLinesIfNeeded` — CSS `avoid-page-break-inside` 实现
|
||||
- 点击测试:`characterIndexAtPoint:` / `lineIndexAtPoint:` / `rectForCharacterAtIndex:`
|
||||
- 选区操作:`selectedText` / `selectedRanges` / `selectTextInRange:` / `clearSelection`
|
||||
- 搜索高亮:`highlightSearchResults:` / `clearSearchHighlights`
|
||||
- 装饰元素:删除线范围、下划线范围、高亮范围、链接范围
|
||||
- 响应式更新:通过 `RACSubject` (ReactiveObjC) 发出布局变更通知
|
||||
|
||||
---
|
||||
|
||||
## 标注系统
|
||||
|
||||
### `WRBookmark`
|
||||
|
||||
书签/标注的数据模型,支持 5 种类型:
|
||||
|
||||
| 类型 | 说明 |
|
||||
|------|------|
|
||||
| `WRBookmarkTypeHighlight` | 彩色高亮 |
|
||||
| `WRBookmarkTypeUnderline` | 下划线 |
|
||||
| `WRBookmarkTypeMark` | 页面书签(折角) |
|
||||
| `WRBookmarkTypeNote` | 文字笔记 |
|
||||
| `WRBookmarkTypePencil` | Apple Pencil 手绘 |
|
||||
|
||||
**关键属性:** `bookmarkId`、`bookId`、`chapterUid`、`chapterOffset`、`markText`、`startPos`/`endPos`、`colorStyle`、`anchorId`、`rangeKey`、`syncKey`
|
||||
|
||||
**持久化:** `toDictionary` / `fromDictionary`、`toJSONData` / `fromJSONData`
|
||||
|
||||
**工厂方法:**
|
||||
- `bookmarkWithBookId:chapterUid:offset:text:type:`
|
||||
- `highlightWithBookId:chapterUid:startPos:endPos:text:colorStyle:`
|
||||
|
||||
### `WRPageHighlight`
|
||||
|
||||
文本高亮标注模型。5 种预设颜色:黄、蓝、红、绿、紫。
|
||||
|
||||
**方法:**
|
||||
- `initWithBookId:chapterUid:startPos:endPos:text:color:` — 创建高亮
|
||||
- `toBookmark` — 转为 WRBookmark 进行持久化/同步
|
||||
- `uiColor` — 返回渲染用 UIColor
|
||||
|
||||
### `WRPageUnderline`
|
||||
|
||||
下划线标注模型。支持 4 种下划线样式:实线、虚线、波浪线、点线。
|
||||
|
||||
**方法:**
|
||||
- `toBookmark` / `fromBookmark:` — 与 WRBookmark 双向转换
|
||||
- `underlinePathForRect:` — 返回渲染用 UIBezierPath
|
||||
- `containsPosition:` — 范围查询
|
||||
- `mergeWithUnderline:` — 合并相邻下划线
|
||||
- `setNote:` — 附加笔记
|
||||
|
||||
### `WRPageMark`
|
||||
|
||||
页面书签(折角)模型。25 个方法,功能最丰富的标注类型。
|
||||
|
||||
**关键特性:**
|
||||
- 支持 3 种展示模式:图标、列表、行内
|
||||
- 关联标注:`linkedHighlights` / `linkedUnderlines` — 书签位置上附带的高亮和下划线
|
||||
- 批量操作:`marksSortedByPosition:` / `marksSortedByDate:` / `marksInChapter:fromMarks:` / `marksInRange:fromMarks:`
|
||||
- 序列化:`toDictionary` / `fromDictionary`、`toJSONData` / `fromJSONData`
|
||||
|
||||
### `WRReaderPencilNoteManager`
|
||||
|
||||
Apple Pencil 手写笔记管理器。11 个类方法,全部为类方法调用。
|
||||
|
||||
**支持 9 种画笔颜色/样式:** 黑、灰、红、蓝、黄、绿、铅笔、钢笔、荧光笔
|
||||
|
||||
**本地存储:**
|
||||
- `drawingFileDirectory` / `drawingFilePathWithReviewItemId:` — 文件路径管理
|
||||
- `writeDrawingDataToLocal:` / `writeDrawingToLocal:` — 本地写入(支持 draft/published 状态)
|
||||
- `checkDrawingExistsWithReviewItemId:` — 存在性检查
|
||||
- `deleteDrawingWithReviewItemId:` / `deleteAllReviewDrawings` — 删除
|
||||
|
||||
**云端同步:**
|
||||
- `uploadPencilDrawing:colorStyle:onlyUploadImage:canRetry:` — 上传绘制图片和数据到腾讯云 COS
|
||||
- `uploadPencilNoteData:suffix:` — 上传 PKDrawing 序列化数据
|
||||
- `downloadDrawingDataFromCosWithUrl:desPath:callback:` — 从 COS 下载
|
||||
|
||||
---
|
||||
|
||||
## DTCoreText 定制模块
|
||||
|
||||
读书基于开源库 [DTCoreText](https://github.com/Cocoanetics/DTCoreText) 做了大量私有定制。
|
||||
|
||||
### `DTHTMLAttributedStringBuilder`
|
||||
|
||||
HTML DOM → `NSAttributedString` 的 SAX 风格解析构建器。`WREpubTypesetter` 的底层依赖。
|
||||
|
||||
**WeRead 自定义属性键:**
|
||||
- `DTPageBackgroundColorAttribute` / `DTPageBackgroundImageAttribute` — 页面背景
|
||||
- `DTPageBreakAfterAttribute` / `DTPageBreakBeforeAttribute` — 分页控制
|
||||
- `DTPageBreakInsideAvoidAttribute` — 跨页避让
|
||||
- `DTPageRelateAttribute` — 跨页关联
|
||||
- `DTHTMLVerticalCenterAttribute` — 垂直居中
|
||||
- `DTHTMLTranslateTagAttribute` — 翻译标签
|
||||
|
||||
### `DTHTMLElement`
|
||||
|
||||
HTML 元素 DOM 节点。构建解析时的 DOM 树,每个节点可通过 `attributedString` 方法生成对应的属性字符串。
|
||||
|
||||
**关键属性:** `tagName`、`parent`/`children`、`styleDictionary`、`isBlockElement`、`shouldAvoidPageBreakInside`
|
||||
|
||||
### `DTCoreTextLayouter` / `DTCoreTextLayoutFrame` / `DTCoreTextLayoutLine` / `DTCoreTextGlyphRun`
|
||||
|
||||
DTCoreText 的四层排版层次结构(与 `WR` 前缀版本功能对应,是底层实现):
|
||||
|
||||
```
|
||||
DTCoreTextLayouter (排版器, CTTypesetter 包装)
|
||||
└── DTCoreTextLayoutFrame (排版帧, CTFrame 包装)
|
||||
└── DTCoreTextLayoutLine (排版行, CTLine 包装)
|
||||
└── DTCoreTextGlyphRun (字形运行, CTRun 包装)
|
||||
```
|
||||
|
||||
- **Layouter** — 创建 CTTypesetter,产出 LayoutFrame,支持帧缓存
|
||||
- **LayoutFrame** — 管理排版行数组,支持多栏、分页避让、点击测试、选区、搜索
|
||||
- **LayoutLine** — 封装 CTLine,管理 GlyphRun 数组,提供排版指标(ascent/descent/leading/width)和点击测试
|
||||
- **GlyphRun** — 封装 CTRun,管理字形(glyph)级别操作:位置、路径绘制、附件(图片)、字体属性、绘制方向
|
||||
|
||||
---
|
||||
|
||||
## 数据流
|
||||
|
||||
```
|
||||
EPUB 文件 (.epub)
|
||||
↓ WREpubParser.parse
|
||||
解析 container.xml → content.opf → toc.ncx
|
||||
↓ WREpubTypesetter.attributeStringWithFilePath
|
||||
XHTML + CSS → DTHTMLAttributedStringBuilder → NSAttributedString
|
||||
↓ WRCoreTextLayouter / WRChapterPageCount
|
||||
分页排版 → [NSRange per page]
|
||||
↓ WRPageView.drawInContext
|
||||
CoreText 绘制到 CGContext
|
||||
↓ WRPageViewController
|
||||
翻页展示
|
||||
```
|
||||
@@ -2,7 +2,7 @@
|
||||
// DTHTMLAttributedStringBuilder.h
|
||||
// DTCoreText (WeRead custom fork)
|
||||
//
|
||||
// Reverse-engineered from WeChat Reading (微信读书) binary.
|
||||
// Reverse-engineered from WeChat Reading (读书) binary.
|
||||
// This class converts HTML DOM into NSAttributedString via SAX-style parsing.
|
||||
//
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// DTHTMLElement.h
|
||||
// DTCoreText (WeRead custom fork)
|
||||
//
|
||||
// Reverse-engineered from WeChat Reading (微信读书) binary.
|
||||
// Reverse-engineered from WeChat Reading (读书) binary.
|
||||
// Represents a single HTML element in the DOM tree built during parsing.
|
||||
// Each node can produce an NSAttributedString via -attributedString.
|
||||
//
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRBookmark.h
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered header
|
||||
//
|
||||
// Bookmark model. Stores bookId, chapterUid, and position information.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRBookmark.m
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered implementation reconstruction
|
||||
//
|
||||
// Detailed pseudo-code based on binary analysis (many NSString ivars,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRChapterData.h
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
//
|
||||
// Reverse-engineered header reconstruction.
|
||||
// Chapter data model. Stores the typeset NSAttributedString.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRChapterData.m
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
//
|
||||
// Detailed pseudo-code reconstruction of the chapter data model.
|
||||
// Stores the typeset NSAttributedString, manages highlights, underlines,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRChapterPageCount.h
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
//
|
||||
// Reverse-engineered header reconstruction.
|
||||
// Pagination calculator. Manages NSRange for each page within a chapter.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRChapterPageCount.m
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
//
|
||||
// Detailed pseudo-code reconstruction of the pagination calculator.
|
||||
// Computes page breaks by simulating CoreText line layout and fitting
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WREpubParser.h
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered header
|
||||
//
|
||||
// EPUB file parser. Parses OPF (content.opf), NCX (toc.ncx), and XHTML chapter files.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WREpubParser.m
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered implementation reconstruction
|
||||
//
|
||||
// Detailed pseudo-code based on binary analysis, ivar types, known methods,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WREpubPositionConverter.h
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered header
|
||||
//
|
||||
// Position converter between file positions and character positions.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WREpubPositionConverter.m
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered implementation reconstruction
|
||||
//
|
||||
// Detailed pseudo-code based on binary analysis, ivar types, known methods,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WREpubTypesetter.h
|
||||
// WeRead (微信读书) - Reverse Engineered
|
||||
// WeRead (读书) - Reverse Engineered
|
||||
//
|
||||
// EPUB Typesetter: Converts XHTML content into NSAttributedString
|
||||
// via DTHTMLAttributedStringBuilder with CSS cascade, image handling,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WREpubTypesetter.m
|
||||
// WeRead (微信读书) - Reverse Engineered Implementation Reconstruction
|
||||
// WeRead (读书) - Reverse Engineered Implementation Reconstruction
|
||||
//
|
||||
// This is a detailed pseudo-code reconstruction based on:
|
||||
// - Binary strings output (method signatures, CSS filenames, attribute names)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRPageHighlight.h
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered header
|
||||
//
|
||||
// Annotation model for text highlights. Stores the highlighted text range,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRPageHighlight.m
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered implementation reconstruction
|
||||
//
|
||||
// 3 methods identified from binary. WRPageHighlight is a lightweight
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRPageMark.h
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered header
|
||||
//
|
||||
// Annotation model for page bookmarks/marks (dog-ear style).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRPageMark.m
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered implementation reconstruction
|
||||
//
|
||||
// 25 methods identified from binary. WRPageMark is the most comprehensive
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRPageUnderline.h
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered header
|
||||
//
|
||||
// Annotation model for text underlines. 10 methods identified from binary.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRPageUnderline.m
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered implementation reconstruction
|
||||
//
|
||||
// 10 methods identified from binary. WRPageUnderline renders underline
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRPageView.h
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
//
|
||||
// Reverse-engineered header reconstruction.
|
||||
// The page rendering view. Inherits UIView. Uses CoreText direct drawing
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRPageView.m
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
//
|
||||
// Detailed pseudo-code reconstruction of the page rendering view.
|
||||
// This view draws text directly via CoreText into CGContext — no UILabel
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRPageViewController.h
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
//
|
||||
// Reverse-engineered header reconstruction.
|
||||
// Based on UIPageViewController. Supports UIPageCurl (simulation) and
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRPageViewController.m
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
//
|
||||
// Detailed pseudo-code reconstruction of the page view controller.
|
||||
// Wraps UIPageViewController with fault detection and patching for
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRPreloadBookManager.h
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered header
|
||||
//
|
||||
// Preload manager. Predicts which chapters to preload based on book ranking
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRPreloadBookManager.m
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered implementation reconstruction
|
||||
//
|
||||
// Detailed pseudo-code based on binary analysis, ivar types (NSMutableDictionary),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRReaderPencilNoteManager.h
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered header
|
||||
//
|
||||
// Apple Pencil note manager. 11 class methods identified from binary.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRReaderPencilNoteManager.m
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
// Reverse-engineered implementation reconstruction
|
||||
//
|
||||
// 11 class methods identified from binary. Manages Apple Pencil
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRReaderViewController.h
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
//
|
||||
// Reverse-engineered header reconstruction.
|
||||
// Main reader controller. Manages reading state, progress saving,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// WRReaderViewController.m
|
||||
// WeRead (微信读书)
|
||||
// WeRead (读书)
|
||||
//
|
||||
// Detailed pseudo-code reconstruction of the main reader controller.
|
||||
// 431 methods total. This file covers the key methods for page rendering,
|
||||
|
||||
@@ -1,439 +0,0 @@
|
||||
+[WRBookNetwork _removeTranslateHtml:]
|
||||
+[WRBookNetwork addMileStone:callback:]
|
||||
+[WRBookNetwork addReview:shareToWechat:audioArticleId:outlineContent:audioColumnId:callback:]
|
||||
+[WRBookNetwork automaticallyMarkFinishReadingWithBookId:callback:]
|
||||
+[WRBookNetwork chaptersInfoFromFile:checkTranslate:]
|
||||
+[WRBookNetwork checkFMCards:callback:]_block_invoke
|
||||
+[WRBookNetwork clearPreloadKVWithBookId:chapterUid:zipPath:]
|
||||
+[WRBookNetwork dislikeReviewById:isDislike:withParams:callback:]
|
||||
+[WRBookNetwork fetchLockInfoWithBookId:callback:]
|
||||
+[WRBookNetwork fileContentWithChapter:book:shouldRemoveHtmlTags:filterTranslateContent:]
|
||||
+[WRBookNetwork fileContentWithChapter:book:shouldRemoveHtmlTags:filterTranslateContent:]_block_invoke
|
||||
+[WRBookNetwork handleUnzipErrorWithPath:plainBookDirectory:]
|
||||
+[WRBookNetwork handleUnzipWithBookId:zipPath:encryptKey:plainBookDirectory:]
|
||||
+[WRBookNetwork handleUnzipWithBookId:zipPath:encryptKey:plainBookDirectory:]_block_invoke
|
||||
+[WRBookNetwork likeReviewById:isUnlike:withParams:callback:]
|
||||
+[WRBookNetwork loadArticleBookDetailWithBookId:callback:]
|
||||
+[WRBookNetwork loadBookDetailPodcastsWithBookId:withSynckey:withListType:withFilterType:withMaxIdx:withCount:]
|
||||
+[WRBookNetwork loadBookInfoWithBookId:source:callback:]
|
||||
+[WRBookNetwork loadBookLectureAuthors:callback:]
|
||||
+[WRBookNetwork loadBookReadDetailInfoWithBookId:callback:]
|
||||
+[WRBookNetwork loadBookReadInfoWithBookId:callback:]
|
||||
+[WRBookNetwork loadBookReadInfoWithBookIdAndVid:vid:callback:]
|
||||
+[WRBookNetwork loadBookmarkListWithBookId:syncKey:callback:]
|
||||
+[WRBookNetwork loadChapterContentWithParam:callback:]
|
||||
+[WRBookNetwork loadChapterContentWithParam:callback:]_block_invoke
|
||||
+[WRBookNetwork loadExchangeRecordListWithParams:callback:]_block_invoke
|
||||
+[WRBookNetwork loadFMCardsWithBookId:withSynckey:withListType:withFilterType:withMaxIdx:withCount:]
|
||||
+[WRBookNetwork loadFriendMarkWithBookId:synckey:]_block_invoke
|
||||
+[WRBookNetwork loadPodcastsWithSynckey:withBookId:withCount:withMaxIdx:callback:]
|
||||
+[WRBookNetwork loadReadTimeWelfareActionWithBookId:opt:secretKey:firstEnter:]
|
||||
+[WRBookNetwork loadRelatedBooksForReviewDetail:callback:]
|
||||
+[WRBookNetwork loadTarForEpubBookId:chapter:isPreload:]
|
||||
+[WRBookNetwork loadTarForEpubBookId:chapter:isPreload:]_block_invoke
|
||||
+[WRBookNetwork loadTarForEpubBookId:chapter:isPreload:]_block_invoke_2
|
||||
+[WRBookNetwork loadTarForEpubBookId:chapter:isPreload:]_block_invoke_5
|
||||
+[WRBookNetwork loadTarForEpubBookId:chapter:isPreload:]_block_invoke_6
|
||||
+[WRBookNetwork loadTarForEpubBookId:chapter:isPreload:]_block_invoke_7
|
||||
+[WRBookNetwork loadTarForEpubBookId:chapters:isPreload:]
|
||||
+[WRBookNetwork loadTopicReviewlist:callback:]
|
||||
+[WRBookNetwork markReadingStatus:bookIds:isCancel:callback:]
|
||||
+[WRBookNetwork markReadingStatus:withBookId:isCancel:withFinishInfo:callback:]
|
||||
+[WRBookNetwork pollingChapterTranslateStatusWithBookId:isFreeTrialActive:referenceLocationDict:chapterTranslations:from:]
|
||||
+[WRBookNetwork postReviewHideWithBookId:hide:callback:]
|
||||
+[WRBookNetwork processChapterInfosFromChapterDownload:bookId:]
|
||||
+[WRBookNetwork processEncryptedBookFileAtPath:encryptKey:book:chapterUid:isFromReview:]
|
||||
+[WRBookNetwork repostReview:reposted:callback:]
|
||||
+[WRBookNetwork resetChapterPaidIfNeededWithBookId:chapterUid:]
|
||||
+[WRBookNetwork rewardReviewForId:price:timestamp:callback:]
|
||||
+[WRBookNetwork savePreloadInfoWithDownloadParam:chaptersStr:timeFlag:tmpFilePath:encryptKey:]
|
||||
+[WRBookNetwork searchResultsForBook:chapterUid:searchString:posBeg:posEnd:mode:callback:]
|
||||
+[WRBookNetwork searchResultsForLocalBook:chapterUid:searchString:posBeg:posEnd:mode:callback:]
|
||||
+[WRBookNetwork setFinishReading:withBookId:callback:]
|
||||
+[WRBookNetwork setIsStartReading:withBookId:callback:]
|
||||
+[WRBookNetwork storeTarEpubImageToDiskWithBookId:chapterUid:untarDirectory:]
|
||||
+[WRBookNetwork uploadBookProgressAndReadingTime:callback:offlineCallback:]
|
||||
+[WRBookNetworkReporter verifyZipWithBook:zipPath:fileSize:unzippedFiles:]
|
||||
+[WRChapterData addUnderLineToAttributedString:range:itemId:style:color:]
|
||||
+[WRChapterData freeTrialChapterCutOffStringLocaionWithAttributedString:book:]
|
||||
+[WRChapterDownloadManger imageManagerForEpubBookWithBookId:]
|
||||
+[WRChapterDownloadManger loadChapterContentNetworkWithParam:callback:]
|
||||
+[WRChapterPageCount currentCacheKeyWithBookId:]
|
||||
+[WRChapterPageCount rangeValueWithPageInfo:]
|
||||
+[WRCoreTextLayouter convertHansToHantWithAttributedString:]_block_invoke
|
||||
+[WREncryptedFileManager decryptContentsOfFile:forBookId:isFileLost:]
|
||||
+[WREncryptedFileManager encryptFileForBookId:originalEncryptKey:atPath:toPath:]
|
||||
+[WREncryptedFileManager keyForBookId:]
|
||||
+[WREpubPositionConverter transformToNumberHtmlEntitesWithOriginHtmlEitites:]_block_invoke
|
||||
+[WREpubTypesetter attributeStringWithFilePath:priority:insertArticleToolAttachment:insertBookChapterToolAttachment:insertRecommendView:book:chapter:pageFlippingStyle:renderErrorReason:isStyleFileNotFound:options:]
|
||||
+[WREpubTypesetter tryReportTranslationError:bookId:chapter:isTranslationStyleNotFound:isTranslationContentNotFound:isTranslateTagButNoTranslateStyle:]
|
||||
+[WRMarkContentStore markContentListForBookId:chapterUid:offset:count:]
|
||||
+[WRMarkContentStore syncMarkContentListForBookId:chapterUid:]
|
||||
+[WRMarketNetwork loadCategoryBooks:param:callback:]
|
||||
+[WRPreloadBookManager clearKV]
|
||||
+[WRPreloadBookManager encryptKeyForPath:bookId:]
|
||||
+[WRPreloadBookManager fileNameForKey:bookId:]
|
||||
+[WRPreloadBookManager removeEncryptKeyForPath:bookId:]
|
||||
+[WRPreloadBookManager removeFileNameForKey:bookId:]
|
||||
+[WRPreloadBookManager removeKVWithBookId:]
|
||||
+[WRPreloadBookManager saveEncryptKey:forPath:bookId:]
|
||||
+[WRPreloadBookManager saveFileNameDict:bookId:]
|
||||
+[WRReaderBackgroundAdapter updateDownloadedBackground]_block_invoke
|
||||
+[WRReaderBackgroundAdapter updateDownloadedBackground]_block_invoke_2
|
||||
+[WRReaderBitmapColorHelper checkImageShouldAddBackgound:imageUrl:completedBlock:]
|
||||
+[WRReaderBitmapColorHelper checkWhiteBlackImageAndTransformToTransparent:completedBlock:]
|
||||
+[WRReaderBitmapColorHelper whiteToTransparentWithImage:completedBlock:]
|
||||
+[WRReaderBookBorrowUtils redeemShortTimeReadWithTargetView:bookId:successBlock:]
|
||||
+[WRReaderBookBorrowUtils redeemShortTimeReadWithTargetView:bookId:successBlock:]_block_invoke
|
||||
+[WRReaderBookBorrowUtils showBorrowConfirmationPanelWithBorrowInfo:confirmBlock:cancelBlock:willShowBlock:]
|
||||
+[WRReaderBookBorrowUtils showBorrowConfirmationPanelWithBorrowInfo:confirmBlock:cancelBlock:willShowBlock:]_block_invoke
|
||||
+[WRReaderBookBorrowUtils showBorrowConfirmationPanelWithBorrowInfo:confirmBlock:cancelBlock:willShowBlock:]_block_invoke_3
|
||||
+[WRReaderCatalogSearchManager appendSearchHistoryWithBookId:keyword:]
|
||||
+[WRReaderCatalogSearchManager purgeSearchHistoryWithBookId:]
|
||||
+[WRReaderCatalogSearchManager searchHistoryWithBookId:]
|
||||
+[WRReaderCht2sManager autoConvertToCht2sStatusWithBook:dataTranslateMode:scene:hasRights:]
|
||||
+[WRReaderCht2sManager canConvertCht2sWithBookId:]
|
||||
+[WRReaderCht2sManager checkCht2sBookShouldSwitchLanguageForBookId:chapterUid:]
|
||||
+[WRReaderContentNavigationManager addRangeValueToOutlineItem:]
|
||||
+[WRReaderContentNavigationManager findParentNodesFromChapterOutlineLeafItems:]_block_invoke
|
||||
+[WRReaderContentNavigationManager rangeForOutlineItem:]
|
||||
+[WRReaderPencilNoteManager authCosForPencilDataWithSuffix:]_block_invoke
|
||||
+[WRReaderPencilNoteManager checkDrawingExistsWithReviewItemId:reviewId:isDraft:]
|
||||
+[WRReaderPencilNoteManager deleteAllReviewDrawings]
|
||||
+[WRReaderPencilNoteManager deleteDrawingWithReviewItemId:reviewId:isDraft:]
|
||||
+[WRReaderPencilNoteManager downloadDrawingDataFromCosWithUrl:desPath:callback:]
|
||||
+[WRReaderPencilNoteManager downloadDrawingDataFromCosWithUrl:desPath:callback:]_block_invoke
|
||||
+[WRReaderPencilNoteManager downloadDrawingWithReviewItemId:reviewId:drawingUrl:dataBlock:]_block_invoke_2
|
||||
+[WRReaderPencilNoteManager downloadDrawingWithReviewItemId:reviewId:drawingUrl:dataBlock:]_block_invoke_3
|
||||
+[WRReaderPencilNoteManager drawingFileDirectory]
|
||||
+[WRReaderPencilNoteManager drawingFilePathWithReviewItemId:reviewId:isDraft:]
|
||||
+[WRReaderPencilNoteManager imageFilePathWithReviewItemId:reviewId:]
|
||||
+[WRReaderPencilNoteManager readDrawingWithReview:dataBlock:drawingUrlBlock:]_block_invoke
|
||||
+[WRReaderPencilNoteManager readDrawingWithReview:dataBlock:drawingUrlBlock:]_block_invoke_2
|
||||
+[WRReaderPencilNoteManager readDrawingWithReview:dataBlock:drawingUrlBlock:]_block_invoke_3
|
||||
+[WRReaderPencilNoteManager uploadPencilDrawing:colorStyle:onlyUploadImage:canRetry:]
|
||||
+[WRReaderPencilNoteManager uploadPencilDrawing:colorStyle:onlyUploadImage:canRetry:]_block_invoke
|
||||
+[WRReaderPencilNoteManager uploadPencilNoteData:suffix:]
|
||||
+[WRReaderPencilNoteManager uploadPencilNoteData:suffix:]_block_invoke
|
||||
+[WRReaderPencilNoteManager uploadPencilNoteData:suffix:]_block_invoke_3
|
||||
+[WRReaderPencilNoteManager writeDrawingDataToLocal:reviewItemId:reviewId:isDraft:]
|
||||
+[WRReaderPencilNoteManager writeDrawingToLocal:reviewItemId:reviewId:isDraft:]
|
||||
+[WRReaderProgress localProgressForBook:]
|
||||
+[WRReaderTranslationManager canSwitchTranslationWithBookId:]
|
||||
+[WRReaderTranslationManager chapterDataCacheKeyWithBookId:chapterUid:]
|
||||
+[WRReaderTranslationManager isPlayChineseWithBookId:chapterUid:]
|
||||
+[WRReaderTranslationManager tryPollingConvertStatusWithBook:isForTranslate:referenceLocationDict:isFreeTrialActive:chapterTranslations:updateTipsBlock:chapterTranslateCompletion:stopPollingBlock:errorBlock:isBatch:from:]_block_invoke
|
||||
+[WRReaderTranslationManager updatePlayTranslation:forBookId:]
|
||||
+[WRReaderTranslationManager wordCountWithBookId:chapterUid:]
|
||||
+[WRReaderUnderlineStyleButtonManager bookmarkColorForColorStyle:underlineStyle:]
|
||||
-[DTCoreTextGlyphRun newPathWithGlyphs]
|
||||
-[DTHTMLAttributedStringBuilder _buildString]
|
||||
-[DTHTMLAttributedStringBuilder _registerTagEndHandlers]_block_invoke_5
|
||||
-[DTHTMLAttributedStringBuilder _registerTagStartHandlers]_block_invoke_11
|
||||
-[DTHTMLAttributedStringBuilder parser:didStartElement:attributes:position:]
|
||||
-[DTHTMLAttributedStringBuilder parser:foundCDATA:]
|
||||
-[DTHTMLAttributedStringBuilder parser:foundCharacters:position:]
|
||||
-[DTHTMLAttributedStringBuilder parserDidEndDocument:]
|
||||
-[DTHTMLElement applyStyleDictionary:isLatinLanguageBook:]
|
||||
-[DTHTMLElement attributedString]
|
||||
-[DTHTMLElement interpretAttributes]
|
||||
-[WRChapter isChapterContentDownloaded]
|
||||
-[WRChapterData addAutoReadUnderLineInRange:style:color:]
|
||||
-[WRChapterData addHighlightInRange:key:itemId:color:]
|
||||
-[WRChapterData addReviewUnderlineInRange:itemId:type:]
|
||||
-[WRChapterData addTempReviewHighlightInRange:itemId:]
|
||||
-[WRChapterData addTempReviewHighlightInRange:itemId:color:]
|
||||
-[WRChapterData deleteReviewUnderlineInRange:type:]
|
||||
-[WRChapterData freeTrialChapterCutOffRealStringLocation]
|
||||
-[WRChapterData generateOutlineContents]
|
||||
-[WRChapterData markFreeTrialChapterCutOffStringLocation:]
|
||||
-[WRChapterData rangeOfPage:]
|
||||
-[WRChapterDownloadManger _preloadChapterContentWithBook:type:bookRank:]
|
||||
-[WRChapterDownloadManger addDownloadTaskCount]
|
||||
-[WRChapterDownloadManger createDownloadTaskSignalWithParam:callback:]_block_invoke
|
||||
-[WRChapterDownloadManger createDownloadTaskSignalWithParam:callback:]_block_invoke_3
|
||||
-[WRChapterDownloadManger downloadComicsChaptersIfNotExistWithParam:withCallback:]
|
||||
-[WRChapterDownloadManger loadChapterContentWithParam:callback:]
|
||||
-[WRChapterDownloadManger networkChanged]
|
||||
-[WRChapterDownloadManger preloadShelfBooksForType:]
|
||||
-[WRChapterDownloadManger removeDownloadTaskCount]
|
||||
-[WRChapterDownloadManger resetChapterDownloadTaskCount]
|
||||
-[WRChapterRecommendAlbumsView handleTouchAlbum:]
|
||||
-[WRChapterRecommendAlbumsView handleTouchAlbum:]_block_invoke
|
||||
-[WRChapterRecommendAlbumsView handleTouchAlbum:]_block_invoke_2
|
||||
-[WRChapterRecommendAlbumsView initWithDataChangeSignal:]_block_invoke
|
||||
-[WRCoreTextLayoutFrame avoidPageBreakInsideByRemovingLastLinesIfNeeded]
|
||||
-[WRCoreTextLayoutFrame drawInContext:image:size:inRect:position:]
|
||||
-[WRCoreTextLayoutFrame getRenderHeight]
|
||||
-[WRCoreTextLayoutFrame lines]
|
||||
-[WRCoreTextLayouter pageBackgroundImageAtRange:themeBgColor:]
|
||||
-[WRCoreTextLayouter resizedImageForImagePath:rect:position:sizePattern:darkMode:themeBgColor:]
|
||||
-[WREpubParser epubController:didFailWithError:]
|
||||
-[WREpubPositionConverter indicesInFile:forRowColumnPairs:stringIndices:string:fileIndexOffset:stringIndexOffset:]
|
||||
-[WREpubPositionConverter initIndices]
|
||||
-[WREpubPositionConverter initWithFilePaths:attributedStrings:offset:isContainIntroFlyleaf:]
|
||||
-[WREpubPositionConverter stringRangeFromFileRange:]
|
||||
-[WRMarketPopToBookShelfTransition animateTransition:]_block_invoke_2
|
||||
-[WRPreloadBookManager _preloadWholeBook:scene:]
|
||||
-[WRPreloadBookManager _preloadWholeBook:scene:]_block_invoke
|
||||
-[WRPreloadBookManager booksDirectoryInfoForNonVIPWithOnlyCalc:]
|
||||
-[WRPreloadBookManager booksDirectoryInfoForVIPWithOnlyCalc:]
|
||||
-[WRPreloadBookManager calcAndClearPreloadBookWithCompletion:onlyCalc:]
|
||||
-[WRPreloadBookManager cleanUpPreloadBook]
|
||||
-[WRPreloadBookManager cleanUpPreloadBook]_block_invoke
|
||||
-[WRPreloadBookManager cleanUpPreloadBook]_block_invoke_2
|
||||
-[WRPreloadBookManager downloadBook:uids:]
|
||||
-[WRPreloadBookManager downloadBook:uids:]_block_invoke
|
||||
-[WRPreloadBookManager downloadingUidsWithBookId:allChapters:fromChapterIdx:]
|
||||
-[WRPreloadBookManager isBookCanPreloadForNonPayingVIP:]
|
||||
-[WRPreloadBookManager isBookCanPreloadForPayingVIP:outReasons:]
|
||||
-[WRPreloadBookManager preloadWithBook:fromChapterIdx:]
|
||||
-[WRPreloadBookManager preloadWithBook:fromChapterIdx:]_block_invoke
|
||||
-[WRPreloadBookManager refreshSetting]
|
||||
-[WRPreloadBookManager remainingDiskSizeInMB]
|
||||
-[WRPreloadBookManager resetBookDataWithBookId:]
|
||||
-[WRPreloadBookManager stopCleanUpPreloadBookTask]
|
||||
-[WRPreloadBookManager unzipBookCacheContentWithBookId:]
|
||||
-[WRPreloadBookManager unzipBookCacheContentWithBookId:]_block_invoke
|
||||
-[WRReaderAIKnowledgeViewController observeValueForKeyPath:ofObject:change:context:]
|
||||
-[WRReaderAuthorReviewDetailViewController handleCommentButtonEvent:]
|
||||
-[WRReaderAuthorReviewDetailViewController handleComposeButtonEvent:]_block_invoke
|
||||
-[WRReaderAuthorReviewDetailViewController handleDeleteComment:]_block_invoke_2
|
||||
-[WRReaderAuthorReviewDetailViewController handlePraiseButtonEvent:]
|
||||
-[WRReaderAuthorReviewDetailViewController handlePraiseButtonEvent:]_block_invoke
|
||||
-[WRReaderAuthorReviewDetailViewController handlePraiseButtonEvent:]_block_invoke_3
|
||||
-[WRReaderAuthorReviewDetailViewController keyboardWillChangeFrameWithUserInfo:]_block_invoke
|
||||
-[WRReaderAuthorReviewDetailViewController tableView:cellForRowAtIndexPath:]
|
||||
-[WRReaderAuthorReviewListViewController handleAvatarButtonEvent:]
|
||||
-[WRReaderAuthorReviewListViewController handlePraiseButtonEvent:]
|
||||
-[WRReaderAuthorReviewListViewController handlePraiseButtonEvent:]_block_invoke
|
||||
-[WRReaderAuthorReviewListViewController handlePraiseButtonEvent:]_block_invoke_3
|
||||
-[WRReaderAuthorReviewListViewController tableView:cellForRowAtIndexPath:]
|
||||
-[WRReaderAutoPlayer calcPageDuration]
|
||||
-[WRReaderAutoPlayer clauseFinished:]
|
||||
-[WRReaderAutoPlayer didBecomeActive]
|
||||
-[WRReaderAutoPlayer initWithDelegate:chapterData:forVerticalReader:]
|
||||
-[WRReaderAutoPlayer pause]
|
||||
-[WRReaderAutoPlayer play]
|
||||
-[WRReaderAutoPlayer setCurrentClauseIndex:]
|
||||
-[WRReaderAutoPlayer setCurrentLocation:]
|
||||
-[WRReaderAutoPlayer setSpeed:]
|
||||
-[WRReaderAutoPlayer setStatus:]
|
||||
-[WRReaderAutoPlayer setTmpClauseIndex:]
|
||||
-[WRReaderAutoPlayer setTmpLocation:]
|
||||
-[WRReaderAutoPlayer setupTimer]
|
||||
-[WRReaderAutoPlayer stop]
|
||||
-[WRReaderAutoPlayer willResignActive]
|
||||
-[WRReaderBackground(WRReaderBackgroundAdapter_Private) downloadWithProgressHandler:completionHandler:]_block_invoke
|
||||
-[WRReaderBookmarkViewController addBookmarkInCurrentReferenceArea]
|
||||
-[WRReaderBookmarkViewController deleteBookmarkWithItemId:callback:]
|
||||
-[WRReaderBrightnessChangeView brightSliderChanged:]
|
||||
-[WRReaderBrightnessChangeView initReaderBackgroundViewUI]_block_invoke
|
||||
-[WRReaderBrightnessChangeView initReaderBackgroundViewUI]_block_invoke_2
|
||||
-[WRReaderBrightnessChangeView sizeThatFits:]
|
||||
-[WRReaderCatalogContainerViewController pageController:didShowPageAtIndex:]_block_invoke
|
||||
-[WRReaderCatalogContainerViewController pageController:titleConfigurationAtIndex:]
|
||||
-[WRReaderCatalogContainerViewController pageController:viewControllerAtIndex:]
|
||||
-[WRReaderCatalogContainerViewController selectCatalogViewController]
|
||||
-[WRReaderCatalogContainerViewController selectOutlineViewController]
|
||||
-[WRReaderCatalogContainerViewController selectSearchContentViewController]
|
||||
-[WRReaderCatalogDataSource initWithBookId:type:delegate:]_block_invoke
|
||||
-[WRReaderCatalogViewController shouldRefreshCurrentAndBackupIndexPathCell]
|
||||
-[WRReaderChangeFirstIndentTableViewCell handleTapItemView:]
|
||||
-[WRReaderChangeOrientationView tableView:cellForRowAtIndexPath:]
|
||||
-[WRReaderChangePageTurningStyleView tableView:cellForRowAtIndexPath:]
|
||||
-[WRReaderCht2sManager updatePlayCht2s:forBookId:]
|
||||
-[WRReaderComicsViewModel chapterReviewForReviewId:]
|
||||
-[WRReaderComicsViewModel contentForChapterUid:]
|
||||
-[WRReaderComicsViewModel imageForURL:]_block_invoke_2
|
||||
-[WRReaderComicsViewModel markAsLiked:forChapterUid:]
|
||||
-[WRReaderComicsViewModel markAsLiked:forReview:]
|
||||
-[WRReaderComicsViewModel preloadNextChapterIfNeededWithCurrentChapterUid:]_block_invoke
|
||||
-[WRReaderComicsViewModel refreshAvailableRangeWithVisibleSectionRange:]
|
||||
-[WRReaderContentNavigationManager didLoadDataWithSusseed:]_block_invoke
|
||||
-[WRReaderContentNavigationManager findCurrentOutlineItemWithUniqId:chapterUid:]
|
||||
-[WRReaderContentNavigationManager findNextOutlineItemInRanges:loadMoreChapterUids:]
|
||||
-[WRReaderContentNavigationManager findPrevOutlineItemInRanges:loadMoreChapterUids:]
|
||||
-[WRReaderContentNavigationManager hasNextOutlineItemInRanges:]
|
||||
-[WRReaderContentNavigationManager hasNextOutlineItem]
|
||||
-[WRReaderContentNavigationManager hasPrevOutlineItemInRanges:]
|
||||
-[WRReaderContentNavigationManager hasPrevOutlineItem]
|
||||
-[WRReaderContentNavigationManager initSearchDataSourceWithBook:]
|
||||
-[WRReaderContentNavigationManager isCurrentOutlineItemVisible]
|
||||
-[WRReaderContentNavigationManager jumpToNextOutlineItemWithPreviousLoadMoreChapterUids:]
|
||||
-[WRReaderContentNavigationManager jumpToNextSearchItem]
|
||||
-[WRReaderContentNavigationManager jumpToOutlineItem:]
|
||||
-[WRReaderContentNavigationManager jumpToOutlineItemWithUniqId:chapterUid:highlight:]
|
||||
-[WRReaderContentNavigationManager jumpToPrevOutlineItemWithPreviousLoadMoreChapterUids:]
|
||||
-[WRReaderContentNavigationManager jumpToPrevSearchItem]
|
||||
-[WRReaderContentNavigationManager jumpToSearchItem:]
|
||||
-[WRReaderContentNavigationManager nextOutlineItemFromMemoryWithLoadMoreChapterUids:]
|
||||
-[WRReaderContentNavigationManager prevOutlineItemFromMemoryWithLoadMoreChapterUids:]
|
||||
-[WRReaderContentNavigationManager searchFrontAndBehindWithKeyword:chapterUid:position:addCurrentItem:callback:]
|
||||
-[WRReaderContentNavigationManager searchFrontAndBehindWithKeyword:chapterUid:position:addCurrentItem:callback:]_block_invoke
|
||||
-[WRReaderContentNavigationManager setCurrentSearchItem:]
|
||||
-[WRReaderContentNavigationManager setupOutlineWithInitialDict:]
|
||||
-[WRReaderContentNavigationManager updateOutlineDataWithBookId:]
|
||||
-[WRReaderDictionaryViewController collectionView:cellForItemAtIndexPath:]
|
||||
-[WRReaderDictionaryViewController observeValueForKeyPath:ofObject:change:context:]
|
||||
-[WRReaderDictionaryViewController refreshData]_block_invoke_4
|
||||
-[WRReaderEndOfTrialButton renderWithLabelDictionary:]
|
||||
-[WRReaderEndOfTrialView handleCampaignButtonTapWithItem:]
|
||||
-[WRReaderEndOfTrialView handleCampaignButtonTapWithItem:]_block_invoke
|
||||
-[WRReaderEndOfTrialView parseRightsViewData]
|
||||
-[WRReaderFloatReviewsViewController initHeaderToolViewIfNeeded]_block_invoke_2
|
||||
-[WRReaderFloatReviewsViewController renderWithHyperlinksInfo:]
|
||||
-[WRReaderFloatReviewsViewController setSelectedText:hyperlinksInfo:]
|
||||
-[WRReaderFontSizeChangeView autoTestHandleChangeValueWithSlider:index:]
|
||||
-[WRReaderFontSizeChangeView sizeThatFits:]
|
||||
-[WRReaderFontSizeChangeView updateFontFamilyChangeButtonFont]
|
||||
-[WRReaderHotUnderlinesViewController updateAppearanceForSectionHeaderView:inTableView:section:isPinned:]
|
||||
-[WRReaderLastPageActionButtonContainerView createFontViewWithFinishIndex:iterationBlock:]
|
||||
-[WRReaderLastPageBadgeView handleCheckButtonDownEvent:]
|
||||
-[WRReaderLastPageBadgeView handleCheckButtonUpEvent:]
|
||||
-[WRReaderLastPageBadgeView handleCheckButtonUpEvent:]_block_invoke_3
|
||||
-[WRReaderLastPageBadgeView handleCheckButtonUpOutSideEvent:]
|
||||
-[WRReaderLastPageViewController refreshFinishedBookInfo]_block_invoke
|
||||
-[WRReaderLastPageViewController rootViewDidChangeIntrinsicSize:]
|
||||
-[WRReaderLastPageViewController showing]
|
||||
-[WRReaderMilestoneLabelView renderWithAttributedStrings:]
|
||||
-[WRReaderMistakeViewController viewDidLoad]_block_invoke
|
||||
-[WRReaderMistakeViewController viewDidLoad]_block_invoke_2
|
||||
-[WRReaderModalView headerViewHeight]
|
||||
-[WRReaderMoreOperationController renderWithBook:scene:]
|
||||
-[WRReaderNoteViewModel initWithBookId:]
|
||||
-[WRReaderOutlineViewController handleJumpToClickContentWithParams:]
|
||||
-[WRReaderOutlineViewController initWithBookId:chapterUid:]_block_invoke
|
||||
-[WRReaderOutlineViewController initWithBookId:chapterUid:]_block_invoke_2
|
||||
-[WRReaderOutlineViewController viewDidLoad]
|
||||
-[WRReaderPanelSelectionView panelHeaderHeight]
|
||||
-[WRReaderPanelSelectionView panelTitle]
|
||||
-[WRReaderPanelSelectionView tableView:cellForRowAtIndexPath:]
|
||||
-[WRReaderPanelSelectionView tableView:didSelectRowAtIndexPath:]
|
||||
-[WRReaderPanelSelectionView tableView:numberOfRowsInSection:]
|
||||
-[WRReaderPencilNoteBaseView handleCanvasViewDrawingDidChange:]
|
||||
-[WRReaderPencilNoteReviewManager editPencilReview:secretMode:imageDict:drawingUrl:didEditBlock:]_block_invoke
|
||||
-[WRReaderPencilNoteReviewManager tryResendAllDraftDrawingReviews]_block_invoke
|
||||
-[WRReaderPencilNoteReviewManager tryResendAllDraftDrawingReviews]_block_invoke_2
|
||||
-[WRReaderPencilNoteReviewManager tryResendAllDraftDrawingReviews]_block_invoke_4
|
||||
-[WRReaderPencilNoteReviewManager tryResendAllDraftDrawingReviews]_block_invoke_5
|
||||
-[WRReaderPencilNoteReviewManager tryResendAllDraftDrawingReviews]_block_invoke_6
|
||||
-[WRReaderPencilNoteReviewManager uploadDrawing:thenEditReview:secretMode:readerVC:didUploadBlock:didEditReviewBlock:didWriteReviewBlock:]_block_invoke
|
||||
-[WRReaderPencilNoteReviewManager writeReview:secretMode:readerVC:imageDict:drawingUrl:didWriteBlock:]
|
||||
-[WRReaderPencilNoteReviewManager writeReview:secretMode:readerVC:imageDict:drawingUrl:didWriteBlock:]_block_invoke
|
||||
-[WRReaderPencilNoteReviewManager writeReview:secretMode:readerVC:imageDict:drawingUrl:didWriteBlock:]_block_invoke_2
|
||||
-[WRReaderPencilNoteViewController addPageReview:]
|
||||
-[WRReaderPencilNoteViewController addRangeReview:]
|
||||
-[WRReaderPencilNoteViewController checkUpdatePencilPageReview:pageNoteView:]
|
||||
-[WRReaderPencilNoteViewController checkUpdatePencilPageReview:pageNoteView:]_block_invoke
|
||||
-[WRReaderPencilNoteViewController checkUpdatePencilPageReview:pageNoteView:]_block_invoke_2
|
||||
-[WRReaderPencilNoteViewController checkUpdatePencilPageReview]
|
||||
-[WRReaderPencilNoteViewController checkUpdatePencilPageReview]_block_invoke
|
||||
-[WRReaderPencilNoteViewController checkUpdatePencilRangeReview]
|
||||
-[WRReaderPencilNoteViewController checkUpdatePencilRangeReview]_block_invoke
|
||||
-[WRReaderPencilNoteViewController checkUpdatePencilRangeReview]_block_invoke_2
|
||||
-[WRReaderPencilNoteViewController checkUpdatePencilRangeReview]_block_invoke_3
|
||||
-[WRReaderPencilNoteViewController checkUpdateRangeReviewDrawingAsDraft]
|
||||
-[WRReaderPencilNoteViewController dealloc]
|
||||
-[WRReaderPencilNoteViewController deleteEmptyDrawingReview:]_block_invoke_2
|
||||
-[WRReaderPencilNoteViewController editPencilReview:secretMode:imageDict:drawingUrl:]_block_invoke
|
||||
-[WRReaderPencilNoteViewController editRangeReviewForColorStyleSilentWithReview:]_block_invoke
|
||||
-[WRReaderPencilNoteViewController editRangeReviewForColorStyleSilentWithReview:]_block_invoke_2
|
||||
-[WRReaderPencilNoteViewController handleBookmarkUpdatedNotification:]
|
||||
-[WRReaderPencilNoteViewController handleCloseRangeReviewEditWithShowAlert:]
|
||||
-[WRReaderPencilNoteViewController handleCloseRangeReviewEditWithShowAlert:]_block_invoke
|
||||
-[WRReaderPencilNoteViewController handleDeleteRangeReview]_block_invoke_2
|
||||
-[WRReaderPencilNoteViewController init]
|
||||
-[WRReaderPencilNoteViewController resetContextWithRangeReviews:chapterIdx:defaultSecretMode:]
|
||||
-[WRReaderPencilNoteViewController setupPageNoteView]_block_invoke
|
||||
-[WRReaderPencilNoteViewController setupRangeNoteView]
|
||||
-[WRReaderPencilNoteViewController setupRangeNoteView]_block_invoke
|
||||
-[WRReaderPencilNoteViewController switchToRangeReview:curRange:curColorStyle:]
|
||||
-[WRReaderPencilPageNoteListView addCanvasViewDrawingDidChangeBlockWithNoteView:]_block_invoke
|
||||
-[WRReaderPencilPageNoteListView autoSaveEditingDrawingToLocalAsDraft]
|
||||
-[WRReaderPencilPageNoteListView handleUpdatePageNoteReviewsWithBlock:]
|
||||
-[WRReaderProgressChangeView handleNextButtonClick:]
|
||||
-[WRReaderProgressChangeView handlePreButtonClick:]
|
||||
-[WRReaderProgressChangeView sizeThatFits:]
|
||||
-[WRReaderProgressData initWithBook:forPDF:]
|
||||
-[WRReaderProgressData toCGIJsonDict]
|
||||
-[WRReaderReviewsRNViewController handleWriteReviewButtonEvent:]
|
||||
-[WRReaderSegmentViewController handleNotesOutputComputerButton]_block_invoke_2
|
||||
-[WRReaderSegmentViewController initNoteControllerIfNeeded]
|
||||
-[WRReaderSinglePurchaseEndTrailBookContentView renderData]
|
||||
-[WRReaderTextSelector updateTurningPageTimerWithGesture:]
|
||||
-[WRReaderTimer handleReadingTime:]
|
||||
-[WRReaderTimer setActive:]
|
||||
-[WRReaderTimer startTimer]
|
||||
-[WRReaderUnderlineStyleButtonManager updateSelectedStyleButtonUI]
|
||||
-[WRReaderViewController _saveReadingProgressAndIsAsync:]
|
||||
-[WRReaderViewController addObservers]_block_invoke
|
||||
-[WRReaderViewController addObservers]_block_invoke_2
|
||||
-[WRReaderViewController animateAfterWritePageReview]_block_invoke
|
||||
-[WRReaderViewController autoTestGetCurrentScreenContentString]
|
||||
-[WRReaderViewController calculateReadingTimeAndUploadIfNeeded]
|
||||
-[WRReaderViewController calculateReadingTimeFromLastReadDate]
|
||||
-[WRReaderViewController canReadFromLocalWithChapterData:]
|
||||
-[WRReaderViewController changeTypesetterAttributesWithBlock:]
|
||||
-[WRReaderViewController checkLocationInCurrentPage:stringLocation:chapterIdx:]
|
||||
-[WRReaderViewController detectExcessPageViews]
|
||||
-[WRReaderViewController didEnterBackGround]
|
||||
-[WRReaderViewController didEnterForeGround]
|
||||
-[WRReaderViewController didFlipPage]
|
||||
-[WRReaderViewController didReceiveMemoryWarning]
|
||||
-[WRReaderViewController firstlyWillRenderPageView:progressData:source:]
|
||||
-[WRReaderViewController gotoChapterIdx:filePosition:pageOfFlyleaf:shouldAutoJumpToCover:]
|
||||
-[WRReaderViewController gotoChapterIdx:position:positionOfFile:]
|
||||
-[WRReaderViewController handleBackProgressButtonClick:]
|
||||
-[WRReaderViewController handleLastPageGesture:]
|
||||
-[WRReaderViewController handleLastPageTapGesture:]
|
||||
-[WRReaderViewController handleSizeChanged]
|
||||
-[WRReaderViewController initChapterPageCount]
|
||||
-[WRReaderViewController initChapterPageCount]_block_invoke
|
||||
-[WRReaderViewController initChapterPageCount]_block_invoke_3
|
||||
-[WRReaderViewController initWithBook:progress:forceUseInitialProgress:doodleMode:autoRead:]
|
||||
-[WRReaderViewController initWithBookId:]
|
||||
-[WRReaderViewController invokeVaildReadingProgress]
|
||||
-[WRReaderViewController isLastPageWithChapterIdx:page:]
|
||||
-[WRReaderViewController isReviewChapterFirstPageWithChapterData:page:]
|
||||
-[WRReaderViewController isReviewChapterInMiddlePageWithChapterData:page:]
|
||||
-[WRReaderViewController isReviewChapterLastPageWithChapterData:page:]
|
||||
-[WRReaderViewController jumpReadingToNeigbourChapterForward:]
|
||||
-[WRReaderViewController jumpReadingToNextChapter]
|
||||
-[WRReaderViewController jumpReadingToPreChapter]
|
||||
-[WRReaderViewController jumpToChapterUid:chapterOffset:shouldAutoJumpToCover:jumpToCloseChapterIdxIfNotExists:jumpFromStartProgress:completion:]
|
||||
-[WRReaderViewController jumpToChapterUid:chapterOffset:shouldAutoJumpToCover:jumpToCloseChapterIdxIfNotExists:jumpFromStartProgress:completion:]_block_invoke
|
||||
-[WRReaderViewController jumpToChapterUid:page:]
|
||||
-[WRReaderViewController loadCurrentPagePencilNotes]
|
||||
-[WRReaderViewController pageView:didClickPencilNote:]
|
||||
-[WRReaderViewController recomposeAndClearCache:]
|
||||
-[WRReaderViewController recomposeCurrentPageViewWithSource:]
|
||||
-[WRReaderViewController recomposeLoadedPageView:]
|
||||
-[WRReaderViewController refreshWithBook:]
|
||||
-[WRReaderViewController reloadCurrentPageView]
|
||||
-[WRReaderViewController reloadPageViewsWithProgressData:source:]
|
||||
-[WRReaderViewController removeAllPageViewLinkBookPaperHighlight]
|
||||
-[WRReaderViewController removeExcessPageViews:pageViewsToKeep:]
|
||||
-[WRReaderViewController renderErrorPageView:error:chapter:andChapterFormat:progressData:source:]
|
||||
-[WRReaderViewController renderLoadingContentPageView:progressData:source:]
|
||||
-[WRReaderViewController renderLoadingContentPageView:progressData:source:]_block_invoke
|
||||
-[WRReaderViewController renderPageView:nextToChapterIdx:page:]
|
||||
-[WRReaderViewController renderPageView:previousToChapterIdx:page:]
|
||||
-[WRReaderViewController renderPageView:progressData:source:]
|
||||
-[WRReaderViewController savePreviousPagePencilNote]
|
||||
-[WRReaderViewController scrollToLastReadingProgressIfNeeded]
|
||||
-[WRReaderViewController setBaseReaderViewModel:]_block_invoke_2
|
||||
-[WRReaderViewController showMileStoreRemind]
|
||||
-[WRReaderViewController startShowingReadingPageView]
|
||||
-[WRReaderViewController syncAuthorFlyleafDatas]
|
||||
-[WRReaderViewController syncBookInfoAndChapterInfoWithCallback:]_block_invoke
|
||||
-[WRReaderViewController syncFlyleapDataWithBook:]
|
||||
-[WRReaderViewController tryToJumpToAuthorFlyLeafPage]
|
||||
-[WRReaderViewController tryToJumpToAuthorFlyLeafPage]_block_invoke
|
||||
-[WRReaderViewController turnToPlayingFileLocation:stringLocation:chapterIdx:]
|
||||
-[WRReaderViewController uploadReadingProgressWithCallback:]
|
||||
-[WRReaderViewController viewDidLoad]
|
||||
-[WRReaderViewController viewWillDisappear:]
|
||||
-[WRReaderViewController willPopInNavigationControllerWithAnimated:]
|
||||
-[WRReaderViewModel loadChapterDataWithChapterIdx:reviewVid:tryRead:callback:]
|
||||
-[WRReaderViewModel updateInfoAfterAutoPaidChapter:]
|
||||
@@ -1,320 +0,0 @@
|
||||
DTCoreText
|
||||
DTCoreTextFontCollection
|
||||
DTCoreTextFontDescriptor
|
||||
DTCoreTextGlyphRun
|
||||
DTCoreTextLayoutFrame
|
||||
DTCoreTextLayoutFrameAccessibilityElementGenerator
|
||||
DTCoreTextLayoutLine
|
||||
DTCoreTextLayouter
|
||||
DTCoreTextParagraphStyle
|
||||
DTHTMLAttributedStringBuilder
|
||||
DTHTMLElement
|
||||
DTHTMLParser
|
||||
DTHTMLParserDelegate
|
||||
DTHTMLParserNode
|
||||
DTHTMLParserTextNode
|
||||
WRBookMarketManager
|
||||
WRBookMarketRankListDataSource
|
||||
WRBookMarketSearchData
|
||||
WRBookMarketSearchDataSource
|
||||
WRBookMarketUIHelper
|
||||
WRBookMarketViewController
|
||||
WRBookNetwork
|
||||
WRBookNetworkReporter
|
||||
WRBookmark
|
||||
WRChapter
|
||||
WRChapterAnchor
|
||||
WRChapterData
|
||||
WRChapterDownloadManger
|
||||
WRChapterDownloadParam
|
||||
WRChapterListener
|
||||
WRChapterPageCount
|
||||
WRChapterReadData
|
||||
WRChapterRecommendAlbumsView
|
||||
WRChapterRecommendAlbumsViewCell
|
||||
WRChapterReviewsDownLoadModel
|
||||
WRCoreTextFloatStatement
|
||||
WRCoreTextImageAttributedStringFormatter
|
||||
WRCoreTextLayoutFrame
|
||||
WRCoreTextLayouter
|
||||
WREncryptedFileManager
|
||||
WREpubPage
|
||||
WREpubParser
|
||||
WREpubPositionConverter
|
||||
WREpubTypesetter
|
||||
WRMarkContent
|
||||
WRMarkContentChapter
|
||||
WRMarkContentInfoViewModel
|
||||
WRMarkContentStore
|
||||
WRMarkContentViewModel
|
||||
WRMarketIcon
|
||||
WRMarketNetwork
|
||||
WRMarketPopToBookShelfTransition
|
||||
WRMarketPopToDiscoverTransition
|
||||
WRMarketPushToSearchTransition
|
||||
WRMarketSearchModel
|
||||
WRMarketSearchScopeTab
|
||||
WRPageAVAttachment
|
||||
WRPageAudioAttachment
|
||||
WRPageAudioView
|
||||
WRPageBookAttachment
|
||||
WRPageBookView
|
||||
WRPageChapterRewardAttachment
|
||||
WRPageChapterRewardView
|
||||
WRPageChapterToolAttachment
|
||||
WRPageChapterToolAttachmentDelegate
|
||||
WRPageChapterToolAttachmentHelper
|
||||
WRPageChapterToolDelegate
|
||||
WRPageChapterToolModel
|
||||
WRPageChapterToolShareTipsView
|
||||
WRPageChapterToolStore
|
||||
WRPageCodeView
|
||||
WRPageCodeViewController
|
||||
WRPageComicView
|
||||
WRPageCountReportManager
|
||||
WRPageCoverAttachment
|
||||
WRPageCoverAttachmentDelegate
|
||||
WRPageCoverOrPageFlyleafAttachmentDelegate
|
||||
WRPageData
|
||||
WRPageElement
|
||||
WRPageFlowCollectionViewLayout
|
||||
WRPageFlyleafAttachment
|
||||
WRPageHighlight
|
||||
WRPageHighlightView
|
||||
WRPageHyperlinksAttachment
|
||||
WRPageHyperlinksAttachmentDelegate
|
||||
WRPageIframeAttachment
|
||||
WRPageIframeView
|
||||
WRPageImageAttachment
|
||||
WRPageImageAttachmentDelegate
|
||||
WRPageImageAttachmentPreviewDelegate
|
||||
WRPageImageAttachmentPreviewViewController
|
||||
WRPageImageView
|
||||
WRPageMark
|
||||
WRPageMarkDelegate
|
||||
WRPageMarkView
|
||||
WRPagePreAttachment
|
||||
WRPageRemindView
|
||||
WRPageSearchHighlight
|
||||
WRPageSliderAttachment
|
||||
WRPageTableAttachment
|
||||
WRPageTextView
|
||||
WRPageTextViewDelegate
|
||||
WRPageTurnModeControl
|
||||
WRPageUnderline
|
||||
WRPageVideoView
|
||||
WRPageView
|
||||
WRPageViewController
|
||||
WRPageViewControllerDelegate
|
||||
WRPageViewDelegate
|
||||
WRPageViewRichElementStub
|
||||
WRPreloadBookManager
|
||||
WRPreloadBookSettingHeaderView
|
||||
WRPreloadBookSettingViewController
|
||||
WRPreloadStatisticsManager
|
||||
WRReaderAIKnowledgeViewController
|
||||
WRReaderAdDialog
|
||||
WRReaderAnchorManager
|
||||
WRReaderAuthorReviewDetailViewController
|
||||
WRReaderAuthorReviewListViewCell
|
||||
WRReaderAuthorReviewListViewController
|
||||
WRReaderAutoPlayer
|
||||
WRReaderAutoPlayerDelegate
|
||||
WRReaderBackground
|
||||
WRReaderBackgroundAdapter
|
||||
WRReaderBackgroundChangeCell
|
||||
WRReaderBackgroundChangeView
|
||||
WRReaderBasePopupCellAdapter
|
||||
WRReaderBitmapColorHelper
|
||||
WRReaderBodyImageHelper
|
||||
WRReaderBookBorrowUtils
|
||||
WRReaderBookIntroControl
|
||||
WRReaderBookReviewStarView
|
||||
WRReaderBookmarkCell
|
||||
WRReaderBookmarkViewController
|
||||
WRReaderBorrowButton
|
||||
WRReaderBrightnessBackgroundButton
|
||||
WRReaderBrightnessChangeView
|
||||
WRReaderBusinessHelper
|
||||
WRReaderCatalogBaseCell
|
||||
WRReaderCatalogBaseCellModel
|
||||
WRReaderCatalogBaseCellView
|
||||
WRReaderCatalogBatchBuyChapterView
|
||||
WRReaderCatalogBookMarkCell
|
||||
WRReaderCatalogBookMarkCellView
|
||||
WRReaderCatalogCell
|
||||
WRReaderCatalogCellUtils
|
||||
WRReaderCatalogContainerViewController
|
||||
WRReaderCatalogCurrentReadingInfo
|
||||
WRReaderCatalogDataSource
|
||||
WRReaderCatalogDataSourceDelegate
|
||||
WRReaderCatalogDataSourceProtocol
|
||||
WRReaderCatalogFooterCell
|
||||
WRReaderCatalogHeaderView
|
||||
WRReaderCatalogItemModel
|
||||
WRReaderCatalogReadingCell
|
||||
WRReaderCatalogReadingCellModel
|
||||
WRReaderCatalogReadingCellView
|
||||
WRReaderCatalogSearchHistoryView
|
||||
WRReaderCatalogSearchManager
|
||||
WRReaderCatalogSectionItem
|
||||
WRReaderCatalogTableHeaderView
|
||||
WRReaderCatalogViewController
|
||||
WRReaderChangeFirstIndentTableViewCell
|
||||
WRReaderChangeFirstIndentTableViewCellItemView
|
||||
WRReaderChangeFirstIndentView
|
||||
WRReaderChangeFirstIndentViewController
|
||||
WRReaderChangeOrientationView
|
||||
WRReaderChangeOrientationViewController
|
||||
WRReaderChangePageTurningStyleView
|
||||
WRReaderChangePageTurningStyleViewController
|
||||
WRReaderCht2sManager
|
||||
WRReaderColorThemeManager
|
||||
WRReaderColorableProtocol
|
||||
WRReaderComicsViewModel
|
||||
WRReaderContentNavigationBottomControl
|
||||
WRReaderContentNavigationControl
|
||||
WRReaderContentNavigationManager
|
||||
WRReaderContentNavigationTopControl
|
||||
WRReaderContext
|
||||
WRReaderControlBarView
|
||||
WRReaderCurProgressTipsView
|
||||
WRReaderDictionaryAIKnowledgeCollectionViewCell
|
||||
WRReaderDictionaryCollectionBaseViewCell
|
||||
WRReaderDictionaryCollectionResultViewCell
|
||||
WRReaderDictionaryCollectionView
|
||||
WRReaderDictionaryEncyclopediaCollectionViewCell
|
||||
WRReaderDictionarySearchCollectionViewCell
|
||||
WRReaderDictionaryTranslationCollectionViewCell
|
||||
WRReaderDictionaryView
|
||||
WRReaderDictionaryViewController
|
||||
WRReaderEndOfTrialButton
|
||||
WRReaderEndOfTrialView
|
||||
WRReaderExchangePushInfo
|
||||
WRReaderExperienceViewController
|
||||
WRReaderFloatReviewsViewController
|
||||
WRReaderFont
|
||||
WRReaderFontChangeCategoryViewController
|
||||
WRReaderFontChangeCategoryViewModel
|
||||
WRReaderFontChangeCell
|
||||
WRReaderFontChangeModalView
|
||||
WRReaderFontChangeShareViewModel
|
||||
WRReaderFontChangeTabsView
|
||||
WRReaderFontChangeViewController
|
||||
WRReaderFontConfig
|
||||
WRReaderFontSizeChangeView
|
||||
WRReaderFontTableViewCell
|
||||
WRReaderFooterPageView
|
||||
WRReaderFooternotePopupView
|
||||
WRReaderHotUnderLinesTotalCountViewController
|
||||
WRReaderHotUnderlinesCell
|
||||
WRReaderHotUnderlinesViewController
|
||||
WRReaderIncentiveButton
|
||||
WRReaderInfoDebuggerViewController
|
||||
WRReaderInfoDebuggerViewModel
|
||||
WRReaderLastPageActionButton
|
||||
WRReaderLastPageActionButtonContainerView
|
||||
WRReaderLastPageBadgeView
|
||||
WRReaderLastPageDelegate
|
||||
WRReaderLastPageHelper
|
||||
WRReaderLastPageMilestoneCardView
|
||||
WRReaderLastPageReviewItem
|
||||
WRReaderLastPageReviewsView
|
||||
WRReaderLastPageViewController
|
||||
WRReaderMaskProtocol
|
||||
WRReaderMaskViewController
|
||||
WRReaderMenuView
|
||||
WRReaderMilestoneLabelView
|
||||
WRReaderMistakeDialogContentView
|
||||
WRReaderMistakeViewController
|
||||
WRReaderModalView
|
||||
WRReaderMoreOperationController
|
||||
WRReaderNoteViewModel
|
||||
WRReaderNotesSectionHeaderView
|
||||
WRReaderOSSLogHelper
|
||||
WRReaderOutlineViewController
|
||||
WRReaderPaidVIPButton
|
||||
WRReaderPanelSelectionView
|
||||
WRReaderPanelSelectionViewController
|
||||
WRReaderPencilNote
|
||||
WRReaderPencilNoteBackgroundView
|
||||
WRReaderPencilNoteBaseView
|
||||
WRReaderPencilNoteButtonGroupView
|
||||
WRReaderPencilNoteEraserTips
|
||||
WRReaderPencilNoteEraserTipsManager
|
||||
WRReaderPencilNoteHelper
|
||||
WRReaderPencilNoteManager
|
||||
WRReaderPencilNoteMaskView
|
||||
WRReaderPencilNoteRangeReviewItem
|
||||
WRReaderPencilNoteRangeReviewsPanel
|
||||
WRReaderPencilNoteReviewManager
|
||||
WRReaderPencilNoteTopToolView
|
||||
WRReaderPencilNoteViewController
|
||||
WRReaderPencilPageNoteListView
|
||||
WRReaderPencilPageNoteView
|
||||
WRReaderPencilRangeNoteView
|
||||
WRReaderPlayerEntranceView
|
||||
WRReaderPopupInfoTableViewCell
|
||||
WRReaderPopupLoadMoreTableViewCell
|
||||
WRReaderPopupReviewTableViewCell
|
||||
WRReaderPresellPageView
|
||||
WRReaderProgress
|
||||
WRReaderProgressButton
|
||||
WRReaderProgressChangeRuleView
|
||||
WRReaderProgressChangeView
|
||||
WRReaderProgressData
|
||||
WRReaderProgressShareView
|
||||
WRReaderProgressSlider
|
||||
WRReaderProgressSliderDelegate
|
||||
WRReaderProgressSmallChangeView
|
||||
WRReaderReadOverToBuyView
|
||||
WRReaderRefreshFooterView
|
||||
WRReaderRefreshHeaderView
|
||||
WRReaderReivewCountLabel
|
||||
WRReaderReviewToolsView
|
||||
WRReaderReviewsRNViewController
|
||||
WRReaderSearchTableHeaderView
|
||||
WRReaderSegmentViewController
|
||||
WRReaderSettingPanelManager
|
||||
WRReaderSettingPanelView
|
||||
WRReaderSettingProtocol
|
||||
WRReaderSinglePurchaseEndTrailBookContentView
|
||||
WRReaderSizeManager
|
||||
WRReaderStatisticPanelViewController
|
||||
WRReaderStatisticRNViewController
|
||||
WRReaderTextSelector
|
||||
WRReaderTimeHelper
|
||||
WRReaderTimer
|
||||
WRReaderTraditionalChineseView
|
||||
WRReaderTranslationManager
|
||||
WRReaderTranslationUpgradeViewController
|
||||
WRReaderUIFactory
|
||||
WRReaderUnderlineStyleButtonManager
|
||||
WRReaderVIPButton
|
||||
WRReaderVIPButtonProtocol
|
||||
WRReaderViewController
|
||||
WRReaderViewControllerDelegate
|
||||
WRReaderViewControllerHelper
|
||||
WRReaderViewModel
|
||||
WRReaderVisiblePageInfo
|
||||
WRReaderVoteView
|
||||
WRReaderWelfareCoinPanel
|
||||
WRReaderWriteReviewSimpleView
|
||||
WRUnderLineView
|
||||
WRUnderline
|
||||
WRUnderlineColorButtonsScrollView
|
||||
WRUnderlineLayoutManager
|
||||
WRUnderlineStore
|
||||
WrBookmarkLoadErrorReport
|
||||
WrBookmarkLoadErrorReportRoot
|
||||
WrPageFlippingAnimationTypeRoot
|
||||
WrReaderActionReport
|
||||
WrReaderActionReportRoot
|
||||
WrReaderActionRoot
|
||||
WrReaderItemtypeRoot
|
||||
WrReaderModuleRoot
|
||||
WrReaderPayingtypeRoot
|
||||
WrReaderSearchAuthoriedFailedTypeRoot
|
||||
WrReaderSearchFailedtypeRoot
|
||||
WrReaderSearchWords
|
||||
WrReaderSearchWordsRoot
|
||||
@@ -1,798 +0,0 @@
|
||||
ivarLayout 0x1031bcf9c
|
||||
layout map 0x11 0x12
|
||||
name 0x1031bcf89 DTCoreTextLayouter
|
||||
baseMethods 0x102acf508
|
||||
entsize 12 (relative)
|
||||
count 12
|
||||
name 0x104ef88 (0x103b1e498)
|
||||
types 0x95e950 (0x10342de64) @24@0:8@16
|
||||
imp 0xfd5764d8 (0x1000459f0)
|
||||
name 0x103a3ec (0x103b09908)
|
||||
types 0x95e89b (0x10342ddbb) v16@0:8
|
||||
imp 0xfd576560 (0x100045a84)
|
||||
name 0x1058528 (0x103b27a50)
|
||||
types 0x95ff95 (0x10342f4c1) @64@0:8{CGRect={CGPoint=dd}{CGSize=dd}}16{_NSRange=QQ}48
|
||||
imp 0xfd576598 (0x100045ac8)
|
||||
name 0x102881c (0x103af7d50)
|
||||
types 0x95e883 (0x10342ddbb) v16@0:8
|
||||
imp 0xfd576734 (0x100045c70)
|
||||
name 0x10436d0 (0x103b12c10)
|
||||
types 0x95ffb6 (0x10342f4fa) ^{__CTFramesetter=}16@0:8
|
||||
imp 0xfd576754 (0x100045c9c)
|
||||
name 0x1075b84 (0x103b450d0)
|
||||
types 0x95e860 (0x10342ddb0) v24@0:8@16
|
||||
imp 0xfd5767c4 (0x100045d18)
|
||||
name 0x102e2c8 (0x103afd820)
|
||||
types 0x95e84c (0x10342dda8) @16@0:8
|
||||
imp 0xfd57684c (0x100045dac)
|
||||
name 0x108cf3c (0x103b5c4a0)
|
||||
types 0x95e8b5 (0x10342de1d) v20@0:8B16
|
||||
imp 0xfd576848 (0x100045db4)
|
||||
name 0x1094710 (0x103b63c80)
|
||||
types 0x95e9a8 (0x10342df1c) B16@0:8
|
||||
imp 0xfd57689c (0x100045e14)
|
||||
--
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031bcf89 DTCoreTextLayouter
|
||||
baseMethods 0x0
|
||||
baseProtocols 0x0
|
||||
ivars 0x0
|
||||
weakIvarLayout 0x0
|
||||
baseProperties 0x0
|
||||
000000010383bc60 0x103ba2550
|
||||
isa 0x103ba2528
|
||||
superclass 0x0 _OBJC_CLASS_$_NSObject
|
||||
cache 0x0 __objc_empty_cache
|
||||
vtable 0x0
|
||||
data 0x10388b308
|
||||
flags 0x90
|
||||
instanceStart 8
|
||||
instanceSize 8
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031bcf9f TABAnimatedChainManagerImpl
|
||||
baseMethods 0x102acf5a0
|
||||
entsize 12 (relative)
|
||||
count 3
|
||||
name 0x1033af0 (0x103b03098)
|
||||
types 0x95ff9d (0x10342f549) v48@0:8@16@24@?32@40
|
||||
imp 0xfd576cac (0x10004625c)
|
||||
name 0x1033aec (0x103b030a0)
|
||||
types 0x95ffa6 (0x10342f55e) v56@0:8@16@24@?32#40@48
|
||||
imp 0xfd576d1c (0x1000462d8)
|
||||
name 0x103bf00 (0x103b0b4c0)
|
||||
types 0x95e7f7 (0x10342ddbb) v16@0:8
|
||||
imp 0xfd576d9c (0x100046364)
|
||||
baseProtocols 0x10388b260
|
||||
--
|
||||
ivarLayout 0x1031bc939
|
||||
layout map 0x04
|
||||
name 0x1031bfb46 WREpubParser
|
||||
baseMethods 0x102aec440
|
||||
entsize 12 (relative)
|
||||
count 11
|
||||
name 0x1032c20 (0x103b1f068)
|
||||
types 0x9419c3 (0x10342de0f) @32@0:8@16@24
|
||||
imp 0xfd7ec460 (0x1002d88b0)
|
||||
name 0x10459b4 (0x103b31e08)
|
||||
types 0x941ac4 (0x10342df1c) B16@0:8
|
||||
imp 0xfd7ec68c (0x1002d8ae8)
|
||||
name 0x1023418 (0x103b0f878)
|
||||
types 0x9419c4 (0x10342de28) v32@0:8@16@24
|
||||
imp 0xfd7ec6b4 (0x1002d8b1c)
|
||||
name 0x102f9b4 (0x103b1be20)
|
||||
types 0x94199f (0x10342de0f) @32@0:8@16@24
|
||||
imp 0xfd7ed284 (0x1002d96f8)
|
||||
name 0x102f5a0 (0x103b1ba18)
|
||||
types 0x941993 (0x10342de0f) @32@0:8@16@24
|
||||
imp 0xfd7ed468 (0x1002d98e8)
|
||||
name 0x101a8d4 (0x103b06d58)
|
||||
types 0x941987 (0x10342de0f) @32@0:8@16@24
|
||||
imp 0xfd7ed4ec (0x1002d9978)
|
||||
name 0x100ead8 (0x103afaf68)
|
||||
types 0x9454c8 (0x10343195c) v40@0:8@16@24Q32
|
||||
imp 0xfd7ed82c (0x1002d9cc4)
|
||||
name 0x100dc6c (0x103afa108)
|
||||
types 0x94b021 (0x1034374c1) v56@0:8@16@24Q32@40@48
|
||||
imp 0xfd7edbd0 (0x1002da074)
|
||||
name 0x10233c8 (0x103b0f870)
|
||||
types 0x94197c (0x10342de28) v32@0:8@16@24
|
||||
imp 0xfd7ee028 (0x1002da4d8)
|
||||
--
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031bfb46 WREpubParser
|
||||
baseMethods 0x102aec428
|
||||
entsize 12 (relative)
|
||||
count 1
|
||||
name 0x1046580 (0x103b329b0)
|
||||
types 0x941a30 (0x10342de64) @24@0:8@16
|
||||
imp 0xfd7ee188 (0x1002da5c0)
|
||||
baseProtocols 0x1038b61b8
|
||||
count 1
|
||||
list[0] 0x103c17ce8
|
||||
isa 0x0
|
||||
name 0x1031bfb53 KFEpubControllerDelegate
|
||||
protocols 0x1038b6130
|
||||
count 1
|
||||
list[0] 0x103c15580
|
||||
isa 0x0
|
||||
name 0x1031bc9c5 NSObject
|
||||
protocols 0x0
|
||||
instanceMethods 0x103884670
|
||||
entsize 24
|
||||
count 19
|
||||
name 0x103348607 isEqual:
|
||||
types 0x10342de6f B24@0:8@16
|
||||
imp 0x0
|
||||
name 0x1032e9bde class
|
||||
types 0x10342df44 #16@0:8
|
||||
imp 0x0
|
||||
name 0x1033a90f1 self
|
||||
types 0x10342dda8 @16@0:8
|
||||
imp 0x0
|
||||
name 0x1033780e4 performSelector:
|
||||
--
|
||||
ivarLayout 0x1031bfd8e
|
||||
layout map 0x16 0x1c 0x12 0x15 0x11
|
||||
name 0x1031bfd80 WRChapterData
|
||||
baseMethods 0x102aeddf0
|
||||
entsize 12 (relative)
|
||||
count 118
|
||||
name 0x102f158 (0x103b1cf50)
|
||||
types 0x93ffac (0x10342dda8) @16@0:8
|
||||
imp 0xfd809f38 (0x1002f7d38)
|
||||
name 0x101552c (0x103b03330)
|
||||
types 0x940101 (0x10342df09) q16@0:8
|
||||
imp 0xfd809f80 (0x1002f7d8c)
|
||||
name 0x1019920 (0x103b07730)
|
||||
types 0x94150c (0x10342f320) {_NSRange=QQ}16@0:8
|
||||
imp 0xfd80a044 (0x1002f7e5c)
|
||||
name 0x103fd04 (0x103b2db20)
|
||||
types 0x941500 (0x10342f320) {_NSRange=QQ}16@0:8
|
||||
imp 0xfd80a07c (0x1002f7ea0)
|
||||
name 0x1033920 (0x103b21748)
|
||||
types 0x9400dd (0x10342df09) q16@0:8
|
||||
imp 0xfd80a0b0 (0x1002f7ee0)
|
||||
name 0x10353cc (0x103b23200)
|
||||
types 0x94427a (0x1034320b2) B24@0:8q16
|
||||
imp 0xfd80a0bc (0x1002f7ef8)
|
||||
name 0x10353c8 (0x103b23208)
|
||||
types 0x94426e (0x1034320b2) B24@0:8q16
|
||||
imp 0xfd80a158 (0x1002f7fa0)
|
||||
name 0x1035b9c (0x103b239e8)
|
||||
types 0x944262 (0x1034320b2) B24@0:8q16
|
||||
imp 0xfd80a18c (0x1002f7fe0)
|
||||
name 0x1035c60 (0x103b23ab8)
|
||||
types 0x944256 (0x1034320b2) B24@0:8q16
|
||||
imp 0xfd80a1c0 (0x1002f8020)
|
||||
--
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031bfd80 WRChapterData
|
||||
baseMethods 0x102aedd90
|
||||
entsize 12 (relative)
|
||||
count 7
|
||||
name 0x100d288 (0x103afb020)
|
||||
types 0x949a69 (0x103437805) v64@0:8@16{_NSRange=QQ}24q40q48@56
|
||||
imp 0xfd80a52c (0x1002f82cc)
|
||||
name 0x101d21c (0x103b0afc0)
|
||||
types 0x940008 (0x10342ddb0) v24@0:8@16
|
||||
imp 0xfd80a694 (0x1002f8440)
|
||||
name 0x10161e8 (0x103b03f98)
|
||||
types 0x949a74 (0x103437828) B40@0:8{_NSRange=QQ}16Q32
|
||||
imp 0xfd80c140 (0x1002f9ef8)
|
||||
name 0x1078b54 (0x103b66910)
|
||||
types 0x9400a4 (0x10342de64) @24@0:8@16
|
||||
imp 0xfd80c154 (0x1002f9f18)
|
||||
name 0x1036ed8 (0x103b24ca0)
|
||||
types 0x94008a (0x10342de56) B32@0:8@16@24
|
||||
imp 0xfd80cf10 (0x1002face0)
|
||||
name 0x104396c (0x103b31740)
|
||||
types 0x949a6a (0x103437842) {_NSRange=QQ}32@0:8q16@24
|
||||
imp 0xfd80e084 (0x1002fbe60)
|
||||
name 0x1024f08 (0x103b12ce8)
|
||||
types 0x9422b4 (0x103430098) q32@0:8@16@24
|
||||
imp 0xfd80e738 (0x1002fc520)
|
||||
baseProtocols 0x0
|
||||
ivars 0x0
|
||||
weakIvarLayout 0x0
|
||||
baseProperties 0x0
|
||||
000000010383c6c8 0x103ba8d38
|
||||
isa 0x103ba8d60
|
||||
--
|
||||
ivarLayout 0x1031c36ba
|
||||
layout map 0x01 0xa2
|
||||
name 0x1031c36a4 WRCoreTextLayoutFrame
|
||||
baseMethods 0x102b1a760
|
||||
entsize 12 (relative)
|
||||
count 47
|
||||
name 0x1004bd8 (0x103b1f340)
|
||||
types 0x922b0e (0x10343d27a) @80@0:8^{__CTFramesetter=}16@24{_NSRange=QQ}32{CGRect={CGPoint=dd}{CGSize=dd}}48
|
||||
imp 0xfdba1dec (0x1006bc55c)
|
||||
name 0xff9e1c (0x103b14590)
|
||||
types 0x9137ac (0x10342df24) d16@0:8
|
||||
imp 0xfdba1ef4 (0x1006bc670)
|
||||
name 0x10388c8 (0x103b53048)
|
||||
types 0x9143b0 (0x10342eb34) v48@0:8{CGRect={CGPoint=dd}{CGSize=dd}}16
|
||||
imp 0xfdba1ef0 (0x1006bc678)
|
||||
name 0x101dd44 (0x103b384d0)
|
||||
types 0x922b3b (0x10343d2cb) B48@0:8{_NSRange=QQ}16{_NSRange=QQ}32
|
||||
imp 0xfdba2028 (0x1006bc7bc)
|
||||
name 0xfffc48 (0x103b1a3e0)
|
||||
types 0x9136d3 (0x10342de6f) B24@0:8@16
|
||||
imp 0xfdba2034 (0x1006bc7d4)
|
||||
name 0x1007a5c (0x103b22200)
|
||||
types 0x917ade (0x103432286) B32@0:8{_NSRange=QQ}16
|
||||
imp 0xfdba20d0 (0x1006bc87c)
|
||||
name 0xfed5f0 (0x103b07da0)
|
||||
types 0x922b3d (0x10343d2f1) @64@0:8^{__CTTypesetter=}16d24{_NSRange=QQ}32d48@56
|
||||
imp 0xfdba218c (0x1006bc944)
|
||||
name 0x100debc (0x103b28678)
|
||||
types 0x9135e8 (0x10342dda8) @16@0:8
|
||||
imp 0xfdba2f20 (0x1006bd6e4)
|
||||
name 0xfec0d0 (0x103b06898)
|
||||
types 0x91373d (0x10342df09) q16@0:8
|
||||
imp 0xfdba4788 (0x1006bef58)
|
||||
--
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031c36a4 WRCoreTextLayoutFrame
|
||||
baseMethods 0x102b1a650
|
||||
entsize 12 (relative)
|
||||
count 2
|
||||
name 0xfecc98 (0x103b072f0)
|
||||
types 0x922bcf (0x10343d22b) @64@0:8@16@24{_NSRange=QQ}32{_NSRange=QQ}48
|
||||
imp 0xfdba9834 (0x1006c3e94)
|
||||
name 0x10492bc (0x103b63920)
|
||||
types 0x922bef (0x10343d257) B60@0:8@16@24{_NSRange=QQ}32q48B56
|
||||
imp 0xfdba9a84 (0x1006c40f0)
|
||||
baseProtocols 0x0
|
||||
ivars 0x0
|
||||
weakIvarLayout 0x0
|
||||
baseProperties 0x0
|
||||
000000010383d6e0 0x103bb2e28
|
||||
isa 0x103bb2e50
|
||||
superclass 0x0 _OBJC_CLASS_$_QMUINavigationButton
|
||||
cache 0x0 __objc_empty_cache
|
||||
vtable 0x0
|
||||
data 0x103901990
|
||||
flags 0x194 RO_HAS_CXX_STRUCTORS
|
||||
instanceStart 8
|
||||
instanceSize 56
|
||||
reserved 0x0
|
||||
ivarLayout 0x1031bcc1a
|
||||
layout map 0x12
|
||||
name 0x1031c36bd WRReviewNavigationButton
|
||||
baseMethods 0x102b1a9a0
|
||||
entsize 12 (relative)
|
||||
count 15
|
||||
name 0x10025a8 (0x103b1cf50)
|
||||
--
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031c405b DTCoreTextLayoutFrameAccessibilityElementGenerator
|
||||
baseMethods 0x102b22c00
|
||||
entsize 12 (relative)
|
||||
count 7
|
||||
name 0xfd6a98 (0x103af96a0)
|
||||
types 0x90c852 (0x10342f45e) @40@0:8@16@24@?32
|
||||
imp 0xfdc59bb0 (0x10077c7c0)
|
||||
name 0xfd6a94 (0x103af96a8)
|
||||
types 0x91ba43 (0x10343e65b) @48@0:8Q16@24@32@?40
|
||||
imp 0xfdc59ccc (0x10077c8e8)
|
||||
name 0xfeca30 (0x103b0f650)
|
||||
types 0x91194f (0x103434573) v40@0:8@16Q24@?32
|
||||
imp 0xfdc59ee0 (0x10077cb08)
|
||||
name 0xfd6a64 (0x103af9690)
|
||||
types 0x91ba40 (0x10343e670) @72@0:8@16{_NSRange=QQ}24@40@48@56@?64
|
||||
imp 0xfdc5a1b0 (0x10077cde4)
|
||||
name 0xfd6a50 (0x103af9688)
|
||||
types 0x91ba5b (0x10343e697) @64@0:8@16{_NSRange=QQ}24@40@48@56
|
||||
imp 0xfdc5a2d4 (0x10077cf14)
|
||||
name 0x104f19c (0x103b71de0)
|
||||
types 0x90c807 (0x10342f44f) @32@0:8@16@?24
|
||||
imp 0xfdc5a468 (0x10077d0b4)
|
||||
name 0xfeff18 (0x103b12b68)
|
||||
types 0x914fd8 (0x103437c2c) {CGRect={CGPoint=dd}{CGSize=dd}}24@0:8@16
|
||||
imp 0xfdc5a490 (0x10077d0e8)
|
||||
baseProtocols 0x0
|
||||
ivars 0x0
|
||||
weakIvarLayout 0x0
|
||||
baseProperties 0x0
|
||||
Meta Class
|
||||
isa 0x0 _OBJC_METACLASS_$_NSObject
|
||||
--
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031c405b DTCoreTextLayoutFrameAccessibilityElementGenerator
|
||||
baseMethods 0x0
|
||||
baseProtocols 0x0
|
||||
ivars 0x0
|
||||
weakIvarLayout 0x0
|
||||
baseProperties 0x0
|
||||
000000010383d9a0 0x103bb49a8
|
||||
isa 0x103bb49d0
|
||||
superclass 0x0 _OBJC_CLASS_$_UICollectionView
|
||||
cache 0x0 __objc_empty_cache
|
||||
vtable 0x0
|
||||
data 0x10390e140
|
||||
flags 0x194 RO_HAS_CXX_STRUCTORS
|
||||
instanceStart 8
|
||||
instanceSize 48
|
||||
reserved 0x0
|
||||
ivarLayout 0x1031bcd23
|
||||
layout map 0x11
|
||||
name 0x1031c408e WRDiscoverCollectionView
|
||||
baseMethods 0x102b22c60
|
||||
entsize 12 (relative)
|
||||
count 20
|
||||
name 0x1001838 (0x103b244a0)
|
||||
types 0x90b2b0 (0x10342df1c) B16@0:8
|
||||
imp 0xfdc5a9b8 (0x10077d628)
|
||||
name 0x101ef44 (0x103b41bb8)
|
||||
types 0x91ba42 (0x10343e6ba) v36@0:8Q16B24@?28
|
||||
imp 0xfdc5aa5c (0x10077d6d8)
|
||||
name 0x1049f78 (0x103b6cbf8)
|
||||
types 0x90b1eb (0x10342de6f) B24@0:8@16
|
||||
imp 0xfdc5abc0 (0x10077d848)
|
||||
--
|
||||
ivarLayout 0x1031c51df
|
||||
layout map 0x12 0x11 0x21
|
||||
name 0x1031c51cc WRChapterPageCount
|
||||
baseMethods 0x102b2ed70
|
||||
entsize 12 (relative)
|
||||
count 29
|
||||
name 0xfefcf8 (0x103b1ea70)
|
||||
types 0x910f9d (0x10343fd19) @40@0:8@16Q24q32
|
||||
imp 0xfdd88e88 (0x1008b7c08)
|
||||
name 0x100981c (0x103b385a0)
|
||||
types 0x908ad4 (0x10343785c) {_NSRange=QQ}24@0:8q16
|
||||
imp 0xfdd892a4 (0x1008b8030)
|
||||
name 0xff70c8 (0x103b25e58)
|
||||
types 0x8ff014 (0x10342dda8) @16@0:8
|
||||
imp 0xfdd89500 (0x1008b8298)
|
||||
name 0x1023294 (0x103b52030)
|
||||
types 0x8ff010 (0x10342ddb0) v24@0:8@16
|
||||
imp 0xfdd89504 (0x1008b82a8)
|
||||
name 0xfd4710 (0x103b034b8)
|
||||
types 0x8feffc (0x10342dda8) @16@0:8
|
||||
imp 0xfdd89504 (0x1008b82b4)
|
||||
name 0x10190fc (0x103b47eb0)
|
||||
types 0x8feff8 (0x10342ddb0) v24@0:8@16
|
||||
imp 0xfdd89508 (0x1008b82c4)
|
||||
name 0xfd4650 (0x103b03410)
|
||||
types 0x8ff145 (0x10342df09) q16@0:8
|
||||
imp 0xfdd89508 (0x1008b82d0)
|
||||
name 0x1019084 (0x103b47e50)
|
||||
types 0x8ff141 (0x10342df11) v24@0:8q16
|
||||
imp 0xfdd8950c (0x1008b82e0)
|
||||
name 0xfd0fb0 (0x103affd88)
|
||||
types 0x8fefcc (0x10342dda8) @16@0:8
|
||||
imp 0xfdd89510 (0x1008b82f0)
|
||||
--
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031c51cc WRChapterPageCount
|
||||
baseMethods 0x102b2ece0
|
||||
entsize 12 (relative)
|
||||
count 11
|
||||
name 0xff2708 (0x103b213f0)
|
||||
types 0x8ff0bc (0x10342dda8) @16@0:8
|
||||
imp 0xfddc1db4 (0x1008f0aa4)
|
||||
name 0xfdab1c (0x103b09810)
|
||||
types 0x8ff0b0 (0x10342dda8) @16@0:8
|
||||
imp 0xfddc1dc4 (0x1008f0ac0)
|
||||
name 0x103b978 (0x103b6a678)
|
||||
types 0x8ff0a4 (0x10342dda8) @16@0:8
|
||||
imp 0xfddc1dc4 (0x1008f0acc)
|
||||
name 0x10063cc (0x103b350d8)
|
||||
types 0x8ff098 (0x10342dda8) @16@0:8
|
||||
imp 0xfddc1dc4 (0x1008f0ad8)
|
||||
name 0x10046e8 (0x103b33400)
|
||||
types 0x8ff08c (0x10342dda8) @16@0:8
|
||||
imp 0xfddc1dc4 (0x1008f0ae4)
|
||||
name 0xfdc50c (0x103b0b230)
|
||||
types 0x8ff080 (0x10342dda8) @16@0:8
|
||||
imp 0xfddc1df0 (0x1008f0b1c)
|
||||
name 0x10027f0 (0x103b31520)
|
||||
types 0x910fe5 (0x10343fd19) @40@0:8@16Q24q32
|
||||
imp 0xfdd89030 (0x1008b7d68)
|
||||
name 0xfd982c (0x103b08568)
|
||||
types 0x9006b8 (0x10342f3f8) Q24@0:8@16
|
||||
imp 0xfdd89128 (0x1008b7e6c)
|
||||
name 0xfee0c0 (0x103b1ce08)
|
||||
types 0x8ff05c (0x10342dda8) @16@0:8
|
||||
imp 0xfdd892d4 (0x1008b8024)
|
||||
--
|
||||
ivarLayout 0x1031c6257
|
||||
layout map 0x04 0x15 0x28
|
||||
name 0x1031c624c WRPageView
|
||||
baseMethods 0x102b3b018
|
||||
entsize 12 (relative)
|
||||
count 104
|
||||
name 0xfe4310 (0x103b1f330)
|
||||
types 0x9064ba (0x1034414de) @64@0:8{CGRect={CGPoint=dd}{CGSize=dd}}16Q48q56
|
||||
imp 0xfde85418 (0x1009c0440)
|
||||
name 0x10294e4 (0x103b64510)
|
||||
types 0x8f2eec (0x10342df1c) B16@0:8
|
||||
imp 0xfde854e0 (0x1009c0514)
|
||||
name 0x102a918 (0x103b65950)
|
||||
types 0x8f2d7f (0x10342ddbb) v16@0:8
|
||||
imp 0xfde85584 (0x1009c05c4)
|
||||
name 0xfe0064 (0x103b1b0a8)
|
||||
types 0x8f2d73 (0x10342ddbb) v16@0:8
|
||||
imp 0xfde85704 (0x1009c0750)
|
||||
name 0xfe0068 (0x103b1b0b8)
|
||||
types 0x8f2d67 (0x10342ddbb) v16@0:8
|
||||
imp 0xfde85770 (0x1009c07c8)
|
||||
name 0xfe2eec (0x103b1df48)
|
||||
types 0x8f2d5b (0x10342ddbb) v16@0:8
|
||||
imp 0xfde85798 (0x1009c07fc)
|
||||
name 0xfe0048 (0x103b1b0b0)
|
||||
types 0x8f2d4f (0x10342ddbb) v16@0:8
|
||||
imp 0xfde85af0 (0x1009c0b60)
|
||||
name 0xfe2ac4 (0x103b1db38)
|
||||
types 0x8f2d43 (0x10342ddbb) v16@0:8
|
||||
imp 0xfde85ca8 (0x1009c0d24)
|
||||
name 0xfe66d8 (0x103b21758)
|
||||
types 0x8f2d24 (0x10342dda8) @16@0:8
|
||||
imp 0xfde85df8 (0x1009c0e80)
|
||||
--
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031c624c WRPageView
|
||||
baseMethods 0x102b3b000
|
||||
entsize 12 (relative)
|
||||
count 1
|
||||
name 0xff1cb8 (0x103b2ccc0)
|
||||
types 0x8f6062 (0x10343106e) d32@0:8@16@24
|
||||
imp 0xfde86674 (0x1009c1684)
|
||||
baseProtocols 0x0
|
||||
ivars 0x0
|
||||
weakIvarLayout 0x0
|
||||
baseProperties 0x0
|
||||
000000010383e338 0x103bba998
|
||||
isa 0x103bba9c0
|
||||
superclass 0x103bc8688
|
||||
cache 0x0 __objc_empty_cache
|
||||
vtable 0x0
|
||||
data 0x103932f00
|
||||
flags 0x194 RO_HAS_CXX_STRUCTORS
|
||||
instanceStart 16
|
||||
instanceSize 32
|
||||
reserved 0x0
|
||||
ivarLayout 0x1031bc967
|
||||
layout map 0x02
|
||||
name 0x1031c625b WRConfirmH5PayViewController
|
||||
baseMethods 0x102b3b500
|
||||
entsize 12 (relative)
|
||||
count 15
|
||||
name 0x10011c8 (0x103b3c6d0)
|
||||
types 0x8f28af (0x10342ddbb) v16@0:8
|
||||
imp 0xfde90aa0 (0x1009cbfb0)
|
||||
name 0xfedbb4 (0x103b290c8)
|
||||
--
|
||||
ivarLayout 0x1031c646d
|
||||
layout map 0x44 0x81 0x31
|
||||
name 0x1031c6457 DTCoreTextLayoutFrame
|
||||
baseMethods 0x102b3ca60
|
||||
entsize 12 (relative)
|
||||
count 54
|
||||
name 0xfc84e8 (0x103b04f50)
|
||||
types 0x8f4143 (0x103430baf) q32@0:8{CGPoint=dd}16
|
||||
imp 0xfdd403fc (0x10087ce6c)
|
||||
name 0xfcc3a4 (0x103b08e18)
|
||||
types 0x900b89 (0x10343d601) {CGRect={CGPoint=dd}{CGSize=dd}}24@0:8q16
|
||||
imp 0xfdd406b8 (0x10087d134)
|
||||
name 0xfe2878 (0x103b1f2f8)
|
||||
types 0x904d71 (0x1034417f5) @72@0:8{CGRect={CGPoint=dd}{CGSize=dd}}16@48{_NSRange=QQ}56
|
||||
imp 0xfdeb5da4 (0x1009f282c)
|
||||
name 0xfe2864 (0x103b1f2f0)
|
||||
types 0x8fdd25 (0x10343a7b5) @56@0:8{CGRect={CGPoint=dd}{CGSize=dd}}16@48
|
||||
imp 0xfdeb5f20 (0x1009f29b4)
|
||||
name 0xfcce70 (0x103b09908)
|
||||
types 0x8f131f (0x10342ddbb) v16@0:8
|
||||
imp 0xfdeb5f20 (0x1009f29c0)
|
||||
name 0xfce904 (0x103b0b3a8)
|
||||
types 0x8f1300 (0x10342dda8) @16@0:8
|
||||
imp 0xfdeb5f6c (0x1009f2a18)
|
||||
name 0xfbacd8 (0x103af7788)
|
||||
types 0x900b34 (0x10343d5e8) {CGPoint=dd}32@0:8@16@24
|
||||
imp 0xfdeb5fa4 (0x1009f2a5c)
|
||||
name 0xfbacdc (0x103af7798)
|
||||
types 0x8f2c9e (0x10342f75e) d24@0:8@16
|
||||
imp 0xfdeb6274 (0x1009f2d38)
|
||||
name 0xfbacc8 (0x103af7790)
|
||||
types 0x900b1c (0x10343d5e8) {CGPoint=dd}32@0:8@16@24
|
||||
imp 0xfdeb6364 (0x1009f2e34)
|
||||
--
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031c6457 DTCoreTextLayoutFrame
|
||||
baseMethods 0x102b3ca40
|
||||
entsize 12 (relative)
|
||||
count 2
|
||||
name 0x101fb08 (0x103b5c550)
|
||||
types 0x8f13d1 (0x10342de1d) v20@0:8B16
|
||||
imp 0xfdeba310 (0x1009f6d60)
|
||||
name 0x10273a4 (0x103b63df8)
|
||||
types 0x8f14c4 (0x10342df1c) B16@0:8
|
||||
imp 0xfdeba310 (0x1009f6d6c)
|
||||
baseProtocols 0x0
|
||||
ivars 0x0
|
||||
weakIvarLayout 0x0
|
||||
baseProperties 0x0
|
||||
000000010383e3c8 0x103bbaf38
|
||||
isa 0x103bbaf60
|
||||
superclass 0x0 _OBJC_CLASS_$_UIView
|
||||
cache 0x0 __objc_empty_cache
|
||||
vtable 0x0
|
||||
data 0x103935920
|
||||
flags 0x194 RO_HAS_CXX_STRUCTORS
|
||||
instanceStart 8
|
||||
instanceSize 280
|
||||
reserved 0x0
|
||||
ivarLayout 0x1031c64b4
|
||||
layout map 0x02 0xb9
|
||||
name 0x1031c647c WHLyricPageTextView
|
||||
baseMethods 0x102b3ccf0
|
||||
entsize 12 (relative)
|
||||
count 60
|
||||
name 0xfe0258 (0x103b1cf50)
|
||||
--
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031c67a2 WREpubTypesetter
|
||||
baseMethods 0x0
|
||||
baseProtocols 0x0
|
||||
ivars 0x0
|
||||
weakIvarLayout 0x0
|
||||
baseProperties 0x0
|
||||
Meta Class
|
||||
isa 0x0 _OBJC_METACLASS_$_NSObject
|
||||
superclass 0x0 _OBJC_METACLASS_$_NSObject
|
||||
cache 0x0 __objc_empty_cache
|
||||
vtable 0x0
|
||||
data 0x10393a6e8
|
||||
flags 0x91 RO_META
|
||||
instanceStart 40
|
||||
instanceSize 40
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031c67a2 WREpubTypesetter
|
||||
baseMethods 0x102b40010
|
||||
entsize 12 (relative)
|
||||
count 9
|
||||
name 0xfbd748 (0x103afd760)
|
||||
types 0x902393 (0x1034423af) @92@0:8@16Q24B32B36B40@44@52q60^q68^B76@84
|
||||
imp 0xfdf0898c (0x100a489ac)
|
||||
name 0x102d67c (0x103b6d6a0)
|
||||
types 0x9023b2 (0x1034423da) v52@0:8@16@24@32B40B44B48
|
||||
imp 0xfdf09b70 (0x100a49b9c)
|
||||
name 0xfc41f8 (0x103b04228)
|
||||
types 0x9023c0 (0x1034423f4) v40@0:8@16^B24^B32
|
||||
imp 0xfdf0a380 (0x100a4a3b8)
|
||||
name 0xfe4fbc (0x103b24ff8)
|
||||
types 0x8ede2f (0x10342de6f) B24@0:8@16
|
||||
imp 0xfdf0a5b8 (0x100a4a5fc)
|
||||
name 0xfc9180 (0x103b091c8)
|
||||
types 0x8ede18 (0x10342de64) @24@0:8@16
|
||||
imp 0xfdf0a644 (0x100a4a694)
|
||||
name 0xfc917c (0x103b091d0)
|
||||
types 0x8ede0c (0x10342de64) @24@0:8@16
|
||||
imp 0xfdf0a754 (0x100a4a7b0)
|
||||
name 0xfc9178 (0x103b091d8)
|
||||
types 0x8ede97 (0x10342defb) @28@0:8@16B24
|
||||
imp 0xfdf0a864 (0x100a4a8cc)
|
||||
name 0xfe3dbc (0x103b23e28)
|
||||
types 0x8eddff (0x10342de6f) B24@0:8@16
|
||||
imp 0xfdf0aa10 (0x100a4aa84)
|
||||
name 0xfc9170 (0x103b091e8)
|
||||
types 0x8edd93 (0x10342de0f) @32@0:8@16@24
|
||||
imp 0xfdf0aa70 (0x100a4aaf0)
|
||||
--
|
||||
ivarLayout 0x1031bc9ce
|
||||
layout map 0x13
|
||||
name 0x1031c67c4 WRCoreTextLayouter
|
||||
baseMethods 0x102b40160
|
||||
entsize 12 (relative)
|
||||
count 15
|
||||
name 0xfbd6f8 (0x103afd860)
|
||||
types 0x8edc3c (0x10342dda8) @16@0:8
|
||||
imp 0xfdf0b9a8 (0x100a4bb18)
|
||||
name 0xfde32c (0x103b1e4a0)
|
||||
types 0x8edc97 (0x10342de0f) @32@0:8@16@24
|
||||
imp 0xfdf0ba50 (0x100a4bbcc)
|
||||
name 0xfd2a90 (0x103b12c10)
|
||||
types 0x8ef376 (0x10342f4fa) ^{__CTFramesetter=}16@0:8
|
||||
imp 0xfdf0badc (0x100a4bc64)
|
||||
name 0xfc7b5c (0x103b07ce8)
|
||||
types 0x902277 (0x103442407) @72@0:8{_NSRange=QQ}16{CGRect={CGPoint=dd}{CGSize=dd}}32Q64
|
||||
imp 0xfdf0bb4c (0x100a4bce0)
|
||||
name 0x1004f38 (0x103b450d0)
|
||||
types 0x8edc14 (0x10342ddb0) v24@0:8@16
|
||||
imp 0xfdf0bc0c (0x100a4bdac)
|
||||
name 0xff1274 (0x103b31418)
|
||||
types 0x8f7c6e (0x103437e16) @40@0:8{_NSRange=QQ}16@32
|
||||
imp 0xfdf0c034 (0x100a4c1e0)
|
||||
name 0xff1270 (0x103b31420)
|
||||
types 0x8f7c62 (0x103437e16) @40@0:8{_NSRange=QQ}16@32
|
||||
imp 0xfdf0c1ac (0x100a4c364)
|
||||
name 0xfd0444 (0x103b10600)
|
||||
types 0x8edbe8 (0x10342dda8) @16@0:8
|
||||
imp 0xfdf0c410 (0x100a4c5d4)
|
||||
name 0xfff160 (0x103b3f328)
|
||||
types 0x902277 (0x103442443) @84@0:8@16{CGRect={CGPoint=dd}{CGSize=dd}}24Q56Q64B72@76
|
||||
imp 0xfdf0c420 (0x100a4c5f0)
|
||||
--
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031c67c4 WRCoreTextLayouter
|
||||
baseMethods 0x102b40118
|
||||
entsize 12 (relative)
|
||||
count 5
|
||||
name 0x1028e50 (0x103b68f70)
|
||||
types 0x8edd4b (0x10342de6f) B24@0:8@16
|
||||
imp 0xfdf0b47c (0x100a4b5a4)
|
||||
name 0xfe54fc (0x103b25628)
|
||||
types 0x8edd3f (0x10342de6f) B24@0:8@16
|
||||
imp 0xfdf0b7cc (0x100a4b900)
|
||||
name 0x10300e8 (0x103b70220)
|
||||
types 0x8f10a1 (0x1034311dd) v28@0:8B16@20
|
||||
imp 0xfdf0b8b8 (0x100a4b9f8)
|
||||
name 0xfc6ebc (0x103b07000)
|
||||
types 0x8edd1c (0x10342de64) @24@0:8@16
|
||||
imp 0xfdf0bd08 (0x100a4be54)
|
||||
name 0xfc6eb8 (0x103b07008)
|
||||
types 0x8edd10 (0x10342de64) @24@0:8@16
|
||||
imp 0xfdf0c020 (0x100a4c178)
|
||||
baseProtocols 0x0
|
||||
ivars 0x0
|
||||
weakIvarLayout 0x0
|
||||
baseProperties 0x0
|
||||
000000010383e4b8 0x103bbb898
|
||||
isa 0x103bbb8c0
|
||||
superclass 0x103bb0ee8
|
||||
cache 0x0 __objc_empty_cache
|
||||
vtable 0x0
|
||||
data 0x10393ab58
|
||||
flags 0x194 RO_HAS_CXX_STRUCTORS
|
||||
instanceStart 16
|
||||
--
|
||||
ivarLayout 0x1031c75f6
|
||||
layout map 0x02 0x2f 0x01 0x33 0x11
|
||||
name 0x1031c75c3 DTHTMLAttributedStringBuilder
|
||||
baseMethods 0x102b4aa58
|
||||
entsize 12 (relative)
|
||||
count 25
|
||||
name 0xfd4958 (0x103b1f3b8)
|
||||
types 0x8eb102 (0x103435b66) @40@0:8@16@24^@32
|
||||
imp 0xfdffe5dc (0x100b49044)
|
||||
name 0xfacf14 (0x103af7980)
|
||||
types 0x8e34ac (0x10342df1c) B16@0:8
|
||||
imp 0xfdffe7dc (0x100b49250)
|
||||
name 0xfc8d80 (0x103b137f8)
|
||||
types 0x8e332c (0x10342dda8) @16@0:8
|
||||
imp 0xfdfff100 (0x100b49b80)
|
||||
name 0xfae034 (0x103af8ab8)
|
||||
types 0x8e3333 (0x10342ddbb) v16@0:8
|
||||
imp 0xfdfff124 (0x100b49bb0)
|
||||
name 0xfae020 (0x103af8ab0)
|
||||
types 0x8e3327 (0x10342ddbb) v16@0:8
|
||||
imp 0xfdffff00 (0x100b4a998)
|
||||
name 0xfe7d3c (0x103b327d8)
|
||||
types 0x8f88ae (0x10344334e) v56@0:8@16@24@32{CGPoint=dd}40
|
||||
imp 0xfe000968 (0x100b4b40c)
|
||||
name 0xfe7d20 (0x103b327c8)
|
||||
types 0x8e337c (0x10342de28) v32@0:8@16@24
|
||||
imp 0xfe000efc (0x100b4b9ac)
|
||||
name 0xfe7be4 (0x103b32698)
|
||||
types 0x8e32f8 (0x10342ddb0) v24@0:8@16
|
||||
imp 0xfe0010c8 (0x100b4bb84)
|
||||
name 0xfe7d30 (0x103b327f0)
|
||||
types 0x8e3364 (0x10342de28) v32@0:8@16@24
|
||||
imp 0xfe0015b8 (0x100b4c080)
|
||||
--
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031c75c3 DTHTMLAttributedStringBuilder
|
||||
baseMethods 0x0
|
||||
baseProtocols 0x10394b7f0
|
||||
count 1
|
||||
list[0] 0x103c1bed0
|
||||
isa 0x0
|
||||
name 0x1031c75e1 DTHTMLParserDelegate
|
||||
protocols 0x10394b690
|
||||
count 1
|
||||
list[0] 0x103c15580
|
||||
isa 0x0
|
||||
name 0x1031bc9c5 NSObject
|
||||
protocols 0x0
|
||||
instanceMethods 0x103884670
|
||||
entsize 24
|
||||
count 19
|
||||
name 0x103348607 isEqual:
|
||||
types 0x10342de6f B24@0:8@16
|
||||
imp 0x0
|
||||
name 0x1032e9bde class
|
||||
types 0x10342df44 #16@0:8
|
||||
imp 0x0
|
||||
name 0x1033a90f1 self
|
||||
types 0x10342dda8 @16@0:8
|
||||
imp 0x0
|
||||
name 0x1033780e4 performSelector:
|
||||
types 0x10342df4c @24@0:8:16
|
||||
imp 0x0
|
||||
name 0x103378163 performSelector:withObject:
|
||||
types 0x10342df57 @32@0:8:16@24
|
||||
imp 0x0
|
||||
--
|
||||
ivarLayout 0x1031c0ccf
|
||||
layout map 0x61
|
||||
name 0x1031cb9a9 WRPageViewController
|
||||
baseMethods 0x102b81cf8
|
||||
entsize 12 (relative)
|
||||
count 73
|
||||
name 0xfbd2d0 (0x103b3efd0)
|
||||
types 0x8ac0b7 (0x10342ddbb) v16@0:8
|
||||
imp 0xfe4dedec (0x101060af4)
|
||||
name 0xff0bac (0x103b728b8)
|
||||
types 0x8c707f (0x103448d8f) v72@0:8@16q24{CGPoint=dd}32q48B56B60@?64
|
||||
imp 0xfe4dedf0 (0x101060b04)
|
||||
name 0xfdf8d0 (0x103b615e8)
|
||||
types 0x8b32e5 (0x103435001) v44@0:8@16q24B32@?36
|
||||
imp 0xfe4df5ec (0x10106130c)
|
||||
name 0xf9d194 (0x103b1eeb8)
|
||||
types 0x8bdff1 (0x10343fd19) @40@0:8@16Q24q32
|
||||
imp 0xfe4df984 (0x1010616b0)
|
||||
name 0xfaf870 (0x103b315a0)
|
||||
types 0x8ac1d5 (0x10342df09) q16@0:8
|
||||
imp 0xfe4dfba8 (0x1010618e0)
|
||||
name 0xff0084 (0x103b71dc0)
|
||||
types 0x8ac07b (0x10342ddbb) v16@0:8
|
||||
imp 0xfe4dfbac (0x1010618f0)
|
||||
name 0xff0038 (0x103b71d80)
|
||||
types 0x8ac0d1 (0x10342de1d) v20@0:8B16
|
||||
imp 0xfe4dfcb4 (0x101061a04)
|
||||
name 0xf87bb4 (0x103b09908)
|
||||
types 0x8ac063 (0x10342ddbb) v16@0:8
|
||||
imp 0xfe4dfcf0 (0x101061a4c)
|
||||
name 0xf78590 (0x103afa2f0)
|
||||
types 0x8ac057 (0x10342ddbb) v16@0:8
|
||||
imp 0xfe4dfe08 (0x101061b70)
|
||||
--
|
||||
reserved 0x0
|
||||
ivarLayout 0x0
|
||||
name 0x1031cb9a9 WRPageViewController
|
||||
baseMethods 0x102b81c98
|
||||
entsize 12 (relative)
|
||||
count 7
|
||||
name 0xf9f110 (0x103b20db0)
|
||||
types 0x8ac117 (0x10342ddbb) v16@0:8
|
||||
imp 0xfe4dc830 (0x10105e4d8)
|
||||
name 0xfb0c9c (0x103b32948)
|
||||
types 0x8ac10b (0x10342ddbb) v16@0:8
|
||||
imp 0xfe4dc99c (0x10105e650)
|
||||
name 0xfb0c88 (0x103b32940)
|
||||
types 0x8ac0ff (0x10342ddbb) v16@0:8
|
||||
imp 0xfe4dd264 (0x10105ef24)
|
||||
name 0xfb0c74 (0x103b32938)
|
||||
types 0x8ac0f3 (0x10342ddbb) v16@0:8
|
||||
imp 0xfe4ddb28 (0x10105f7f4)
|
||||
name 0xf89a18 (0x103b0b6e8)
|
||||
types 0x8c708d (0x103448d61) B48@0:8@16@24B32B36B40B44
|
||||
imp 0xfe4de164 (0x10105fe3c)
|
||||
name 0xfa437c (0x103b26058)
|
||||
types 0x8ae3b8 (0x103430098) q32@0:8@16@24
|
||||
imp 0xfe4dedac (0x101060a90)
|
||||
name 0xfe3180 (0x103b64e68)
|
||||
types 0x8ac0c4 (0x10342ddb0) v24@0:8@16
|
||||
imp 0xfe4e15b8 (0x1010632a8)
|
||||
baseProtocols 0x1039a0c00
|
||||
count 4
|
||||
list[0] 0x103c17bc8
|
||||
isa 0x0
|
||||
name 0x1031bf992 UIPageViewControllerDataSource
|
||||
protocols 0x1038b4590
|
||||
@@ -0,0 +1,181 @@
|
||||
# WXRead/resources 文件说明
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
resources/
|
||||
├── js/
|
||||
│ ├── Text/ # 文本编辑/展示相关
|
||||
│ │ ├── WeReadApi.js
|
||||
│ │ ├── rich_editor.js
|
||||
│ │ ├── rich_display.js
|
||||
│ │ ├── correctArticle.js
|
||||
│ │ └── style_html.js
|
||||
│ ├── Readability.js
|
||||
│ ├── weread-highlighter.js
|
||||
│ ├── rangy-core.js
|
||||
│ ├── rangy-classapplier.js
|
||||
│ ├── rangy-highlighter.js
|
||||
│ ├── rangy-textrange.js
|
||||
│ ├── highlight.min.js
|
||||
│ ├── cssInjector.js
|
||||
│ └── patch_6.2.1.4.js
|
||||
├── css/
|
||||
│ ├── Style/
|
||||
│ │ ├── style.css
|
||||
│ │ ├── preview.css
|
||||
│ │ └── normalize.css
|
||||
│ ├── default.css
|
||||
│ ├── dark.css
|
||||
│ ├── replace.css
|
||||
│ ├── replaceForLatinLanguageBook.css
|
||||
│ ├── replaceForMPChapter.css
|
||||
│ ├── MPExtra.css
|
||||
│ ├── MediaPlatform.css
|
||||
│ ├── WREpubMPVideo.css
|
||||
│ └── xcode.min.css
|
||||
└── fonts/ # 字体文件
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## JS 文件
|
||||
|
||||
### 核心功能
|
||||
|
||||
#### `Readability.js`
|
||||
Arc90 的 Readability 库 (v1.7.1),用于从网页中提取正文内容。通过正则匹配和 DOM 分析算法,从杂乱的 HTML 中识别出文章主体,过滤导航、广告、侧栏等非内容元素。是读书解析公众号文章内容的基础依赖。
|
||||
|
||||
#### `weread-highlighter.js`
|
||||
读书的划线/高亮核心模块。依赖 Rangy 库实现以下功能:
|
||||
- 初始化 Rangy 高亮器,注册 `highlight`(划线)、`review`(自己的想法)、`friend-review`(好友想法)、`reference`(想法圈划线)、`tts`(朗读高亮)等样式
|
||||
- 监听 `selectionchange` 事件,将选区位置信息通过 `wereadBridge` 通知原生层
|
||||
- 提供 `getShareAbstractText()` 从文章段落中提取分享摘要文本
|
||||
- 字体缩放 `changeFontSizeWithScale()` 支持
|
||||
- 图片点击事件绑定和 H5 链接转换
|
||||
|
||||
#### `cssInjector.js`
|
||||
CSS 动态注入/移除工具。提供 `inject(json)` 和 `remove(identifier)` 两个方法,通过创建/删除 `<style>` 元素实现运行时样式切换。
|
||||
|
||||
#### `patch_6.2.1.4.js`
|
||||
版本补丁脚本。修改 `WRAboutViewController` 的 `viewDidLoad`,将版权信息文案中的 "rights" 和 "reserved" 首字母改为大写。
|
||||
|
||||
### Rangy 库系列 (第三方)
|
||||
|
||||
#### `rangy-core.js`
|
||||
Rangy 核心库 (v1.3.0, MIT License)。跨浏览器的 Range 和 Selection 操作库,提供统一的 API 处理 DOM 选区。
|
||||
|
||||
#### `rangy-classapplier.js`
|
||||
Rangy ClassApplier 模块。对 Range 和 Selection 添加/移除/切换 CSS 类,是实现划线高亮的基础。
|
||||
|
||||
#### `rangy-highlighter.js`
|
||||
Rangy Highlighter 模块。依赖 core 和 ClassApplier,提供更高层的文本高亮 API,支持序列化/反序列化高亮位置。
|
||||
|
||||
#### `rangy-textrange.js`
|
||||
Rangy TextRange 模块。支持按字符/词偏移移动选区边界、文本搜索、选区保存/恢复等功能。
|
||||
|
||||
#### `highlight.min.js`
|
||||
highlight.js 代码语法高亮库(压缩版)。用于在阅读器中高亮显示 `<pre><code>` 代码块。
|
||||
|
||||
### Text 子目录
|
||||
|
||||
#### `Text/WeReadApi.js`
|
||||
读书 WebView Bridge。定义 `WeReadBridge` 类,通过隐藏 iframe + 自定义 URL scheme(`wereadapijs://`)实现 JS 与原生层的双向通信。支持回调机制和可用功能查询。
|
||||
|
||||
#### `Text/rich_editor.js`
|
||||
富文本编辑器核心模块。定义 `RE` 对象,管理编辑器 DOM 引用和编辑操作。
|
||||
|
||||
#### `Text/rich_display.js`
|
||||
富文本展示模块。定义 `RDisplay` 对象,处理展示模式下的交互(书籍卡片触摸反馈、点击高亮等)。
|
||||
|
||||
#### `Text/correctArticle.js`
|
||||
文章内容校正脚本。用于对公众号文章的 HTML 结构进行清洗和修正。
|
||||
|
||||
#### `Text/style_html.js`
|
||||
JS Beautifier 库。HTML/JS/CSS 代码格式化工具(MIT License),用于代码块的美化显示。
|
||||
|
||||
---
|
||||
|
||||
## CSS 文件
|
||||
|
||||
### 默认/基础样式
|
||||
|
||||
#### `default.css`
|
||||
公众号文章的基础阅读样式。覆盖微信原生样式,定义:
|
||||
- 文章区域布局(`rich_media_area_primary`:padding、字号 18px、行高 30px)
|
||||
- 标题样式(`rich_media_title`:24px/36px)
|
||||
- 内容区域溢出处理、blockquote 样式
|
||||
- 隐藏工具栏(`rich_media_tool`)
|
||||
- 划线/想法/高亮样式(`.highlight` 橙色、`.review` 红色虚线、`.friend-review` 灰色虚线)
|
||||
- 隐藏 iPad 上的 PC 二维码
|
||||
- TTS 朗读高亮(`.tts` 蓝色背景)
|
||||
- 公众号图片链接禁用点击
|
||||
|
||||
#### `dark.css`
|
||||
深色模式样式。全局覆盖为纯黑背景(`rgba(0,0,0,1)`),文字颜色设为灰色(`rgba(180,180,182,1)`),链接为蓝色,blockquote 为深色背景。适配搜狗百科和百度搜索的特殊元素。
|
||||
|
||||
#### `xcode.min.css`
|
||||
代码语法高亮配色方案(highlight.js 的 Xcode 主题),白色背景,关键字紫色、字符串红色、注释绿色等。
|
||||
|
||||
### 内容替换样式
|
||||
|
||||
#### `replace.css`
|
||||
EPUB 书籍的主要排版样式。定义:
|
||||
- 代码块字体(Menlo/Consolas)、版权标题、图片说明文字
|
||||
- 六级标题样式(`.firstTitle` ~ `.sixthTitle`,1.0em~1.5em)
|
||||
- 首字下沉(`.ftext`)、引用(`.conQuot`)、副标题(`.subHead`)
|
||||
- 图片垂直居中(`wr-vertical-center-style`)、翻译行高
|
||||
- 章节工具栏高度(`.book-chapter-tool`:78px)
|
||||
|
||||
#### `replaceForLatinLanguageBook.css`
|
||||
拉丁语系书籍的排版样式。与 `replace.css` 类似,但标题使用 `Source Han Serif CN` 字体并加粗(而非 `SourceHanSerifCN-Medium` normal),适配西文排版习惯。
|
||||
|
||||
#### `replaceForMPChapter.css`
|
||||
公众号文集(小文章)的排版样式。在 `replace.css` 基础上增加了:
|
||||
- `.weread-page-relate` 跨页关联标记
|
||||
- `.chapter-reward` 打赏区域(120px)
|
||||
- `.chapter-tool` 工具栏(140px)
|
||||
- `.re_bookItem` 书籍卡片间距
|
||||
- `img[data-image-size="large"]` 大图全宽
|
||||
- `.unsupported_iframe` 不支持控件的提示样式
|
||||
- 强制清除 `<p>`、`<strong>`、`<span>`、`<section>` 的背景色
|
||||
|
||||
### 公众号专用样式
|
||||
|
||||
#### `MPExtra.css`
|
||||
公众号文章的完整样式表(从微信 CSS 复制并修改)。包含:
|
||||
- 微信 UI 组件样式(`.rich_media_*`、`.weui-*`)
|
||||
- 小程序卡片样式(`.weapp_*`)
|
||||
- GIF 图片播放提示
|
||||
- 全局消息提示栏
|
||||
- 对话框组件(`.weui-dialog`)
|
||||
- 公众号音频播放器样式(`.audio_*`,含 Retina 适配)
|
||||
- 被删公众号链接隐藏处理
|
||||
- iPhone 横屏适配
|
||||
|
||||
#### `MediaPlatform.css`
|
||||
公众号文章阅读的精简样式。覆盖微信原生样式中的关键属性:背景色、字号、行高、blockquote、QQ 音乐区域、分享图片等。主要用于非文集场景的公众号阅读。
|
||||
|
||||
#### `WREpubMPVideo.css`
|
||||
腾讯视频播放器的自定义样式。完全重写视频控制器 UI:
|
||||
- 自定义播放/暂停、全屏按钮图标(使用 CDN sprite 图)
|
||||
- 进度条、时间显示器布局
|
||||
- 隐藏广告和下载提示
|
||||
- 响应式适配(iPhone 6/6P 尺寸)
|
||||
|
||||
### Style 子目录
|
||||
|
||||
#### `Style/style.css`
|
||||
笔记/想法的编辑和展示样式(EPUB 基准 1em=16px)。定义:
|
||||
- 编辑器容器(`.re`)基础样式
|
||||
- 段落、标题、列表、blockquote 样式
|
||||
- 书籍卡片组件(`.re_bookItem`,含 Retina 边框)
|
||||
- 图片样式(`.re_img`、`.bodyPic`)
|
||||
- QQ 表情样式
|
||||
- 深色模式(`.re_Night`)
|
||||
|
||||
#### `Style/preview.css`
|
||||
想法/笔记的预览样式。与 `style.css` 类似但适用于阅读展示场景,字号 18px,行高 30px,使用 `-apple-system` 字体。
|
||||
|
||||
#### `Style/normalize.css`
|
||||
normalize.css v3.0.2(MIT License)。标准化各浏览器的默认样式差异。
|
||||
@@ -1,26 +0,0 @@
|
||||
|
||||
var generator = {};
|
||||
|
||||
generator.generateHtml = function (content, js, css) {
|
||||
var result = content;
|
||||
result = result.replace("<!--headTrap<body></body><head></head><html></html>-->", "");
|
||||
result = result.replace("<!--tailTrap<body></body><head></head><html></html>-->", "");
|
||||
var match = result.match(/window\.__nonce_str = "(\d+)"/);
|
||||
var nonce = "";
|
||||
if (match && match.length > 1) {
|
||||
nonce = "nonce='" + match[1] + "'";
|
||||
}
|
||||
var insertJs = "<script " + (nonce.length > 1 ? nonce : "") + " type='text/javascript'>" + js + "</script></body>";
|
||||
var insertStyle = "<style type='text/css'>" + css + "</style></head>";
|
||||
result = result.replace("</head>", insertStyle);
|
||||
result = result.replace("</body>", insertJs);
|
||||
return result;
|
||||
}
|
||||
|
||||
generator.generateHtmlForMPReview = function (content,js, css) {
|
||||
var result = generator.generateHtml(content,js,css)
|
||||
var modifiedCopyRight = " <span id = 'copyright_logo' stype = 'display:none' > ";
|
||||
var reg = /<span id="copyright_logo".*>?/;
|
||||
result = result.replace(reg,modifiedCopyRight);
|
||||
return result;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
(function(){
|
||||
var head = document.getElementsByTagName("head")[0];
|
||||
var meta = document.createElement('meta');
|
||||
meta.name = "viewport";
|
||||
meta.content = "width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no";
|
||||
head.appendChild(meta);
|
||||
|
||||
var children = document.body.children;
|
||||
var titleFounded = false;
|
||||
for(var i = 0; i < children.length; i++){
|
||||
var child = children[i]
|
||||
var tagName = child.tagName.toLowerCase()
|
||||
// 隐藏标题前的空段落
|
||||
if(!child.textContent){
|
||||
child.style.display = "none";
|
||||
}else if(titleFounded){
|
||||
// 如果找到标题了,那么遇到第一个不为空的短路,就不再处理了
|
||||
break
|
||||
}
|
||||
// 隐藏标题
|
||||
if(tagName === 'h1' || tagName === 'h2' || tagName === 'h3'){
|
||||
child.style.display = "none"
|
||||
titleFounded = true
|
||||
}
|
||||
}
|
||||
})()
|
||||
@@ -1,125 +0,0 @@
|
||||
var mediaPlatform = {};
|
||||
|
||||
mediaPlatform.getMPHeight = function() {
|
||||
var height = document.getElementById('js_article').scrollHeight;
|
||||
return height;
|
||||
};
|
||||
|
||||
mediaPlatform.getMPInfo = function() {
|
||||
var title;
|
||||
if (msg_title) {
|
||||
title = msg_title;
|
||||
}
|
||||
else {
|
||||
title = document.getElementsByTagName('h1')[0].innerHTML.toString();
|
||||
}
|
||||
var thumbUrl;
|
||||
if (msg_cdn_url) {
|
||||
thumbUrl = msg_cdn_url;
|
||||
}
|
||||
else {
|
||||
thumbUrl = document.getElementsByTagName('img')[0].src.toString();
|
||||
}
|
||||
var account;
|
||||
if (nickname) {
|
||||
account = nickname;
|
||||
}
|
||||
if (!title) {title = "";}
|
||||
if (!thumbUrl) {thumbUrl= "";}
|
||||
if (!account) {account = "";}
|
||||
|
||||
var info = {
|
||||
"title": title,
|
||||
"thumbUrl": thumbUrl,
|
||||
"account": account,
|
||||
}
|
||||
return info;
|
||||
};
|
||||
|
||||
mediaPlatform.getMPInfoStr = function() {
|
||||
return JSON.stringify(mediaPlatform.getMPInfo());
|
||||
};
|
||||
|
||||
// 这里的做法是为了非独立图片(下方有内容)可以保持跟内容12px的距离
|
||||
mediaPlatform.handleImageBlank = function() {
|
||||
var imgs = document.getElementsByTagName('img');
|
||||
for (var i = 0; i < imgs.length; i++) {
|
||||
var nextNode = imgs[i].nextSibling;
|
||||
var preNode = imgs[i].previousSibling;
|
||||
|
||||
// 只处理上方有空行的情况,下方有空行经常会跟图片的说明文字连用,尝试修改会导致样式错乱
|
||||
if (preNode && preNode.nodeName.toLowerCase() == 'br') {
|
||||
// 处理图片上面有个br,但是br不能自定义高度,所以直接去掉重新加margin
|
||||
if (preNode.style) {
|
||||
preNode.style.display = "none";
|
||||
}
|
||||
imgs[i].style.marginTop = '12px';
|
||||
} else if (preNode && preNode.nodeType == 3 && !this.emptyTextNode(preNode)) {
|
||||
// 处理图片跟上面文字在同一个p里面,但是没有br隔开
|
||||
imgs[i].style.marginTop = '12px';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mediaPlatform.emptyTextNode = function(el) {
|
||||
return !el.data || /^(\s|\t)+$/g.test(el.data)
|
||||
}
|
||||
|
||||
// 干掉p>br的情况的margin-top
|
||||
mediaPlatform.updateBlankHeight = function() {
|
||||
var blanks = document.getElementsByTagName('br');
|
||||
for (var i = blanks.length - 1; i >= 0; i--) {
|
||||
var parentNode = blanks[i].parentNode;
|
||||
if (parentNode.nodeName.toLowerCase() == "p" && parentNode.childNodes.length == 1) {
|
||||
parentNode.style.height = '20px';
|
||||
parentNode.style.minHeight = '0em';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 观察到部分公众号文章首评的懒加载图片无法正常显示,这是由于 scroll 事件没有触发导致的(怀疑是在 rangy-classapplier 引入后产生了混合反应,但在 Android 上确是好的)。这里的解决办法是手动触发一次 scroll 事件,但由于我们的 webview 其实是完整高度的,本身并不能滚动,所以这样做也不会产生抖动或者其他副作用。
|
||||
mediaPlatform.protectLazyLoad = function() {
|
||||
window.scrollTo(0, 1);
|
||||
};
|
||||
|
||||
mediaPlatform.init = function() {
|
||||
mediaPlatform.updateBlankHeight();
|
||||
mediaPlatform.handleImageBlank();
|
||||
mediaPlatform.protectLazyLoad();
|
||||
};
|
||||
|
||||
mediaPlatform.getAuthor = function() {
|
||||
var find = false;
|
||||
var author = document.getElementById('js_name');
|
||||
if (author != null) {
|
||||
console.log('mediaPlatform.getAuthor ' + author.innerText)
|
||||
author.addEventListener('click',function(){
|
||||
wereadBridge.handleWithRichEditor("onClickAuthor",{"param" : author.innerText, "cmd": "onClickAuthor"}, "", "");
|
||||
}, false)
|
||||
}
|
||||
}
|
||||
|
||||
mediaPlatform.init();
|
||||
|
||||
// 用于在退出公众号文章的时候暂停视频播放
|
||||
// 由于腾讯视频是通过 iFrame 内嵌的,没有办法直接操作,我们用刷新 iFrame 的方式实现停止播放的效果
|
||||
function pauseMedia() {
|
||||
pauseCurrentAudio();
|
||||
pauseCurrentVideo();
|
||||
}
|
||||
|
||||
function pauseCurrentAudio() {
|
||||
var audios = document.querySelectorAll('audio');
|
||||
for (var i = 0; i < audios.length; i++) {
|
||||
var audio = audios[i];
|
||||
audio.pause();
|
||||
}
|
||||
}
|
||||
|
||||
function pauseCurrentVideo() {
|
||||
var iframes = document.querySelectorAll('iframe');
|
||||
for (var i = 0; i < iframes.length; i++) {
|
||||
var oldFrame = iframes[i];
|
||||
oldFrame.src = oldFrame.src;
|
||||
}
|
||||
}
|
||||
@@ -1,349 +0,0 @@
|
||||
var WRAUDIO_TEMPLATE = '<div id="wraudioid_xxx" class="audio_container">\
|
||||
<div id="wraudio_button" class="audio_button"></div>\
|
||||
<div class="audio_content">\
|
||||
<p id="wraudio_title" class="audio_title"></p>\
|
||||
<div class="audio_progress">\
|
||||
<div class="audio_progress_bottom"></div>\
|
||||
<div id="wraudio_progress_bar" class="audio_progress_top" style="width: 0;"></div>\
|
||||
<div id="wraudio_progress_button" class="audio_progress_current" style="left: 0;"></div>\
|
||||
</div>\
|
||||
<div class="audio_time">\
|
||||
<span id="wraudio_time_elapsed" class="audio_time_left"></span>\
|
||||
<span id="wraudio_time_remains" class="audio_time_right"></span>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
var _Utils = function () {
|
||||
this.findChildById = function (element, childID) {
|
||||
var retElement = null;
|
||||
var lstChildren = Utils.getAllDescendant(element);
|
||||
|
||||
for (var i = 0; i < lstChildren.length; i++) {
|
||||
if (lstChildren[i].id == childID) {
|
||||
retElement = lstChildren[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return retElement;
|
||||
}
|
||||
|
||||
this.getAllDescendant = function (element, lstChildrenNodes) {
|
||||
lstChildrenNodes = lstChildrenNodes ? lstChildrenNodes : [];
|
||||
|
||||
var lstChildren = element.childNodes;
|
||||
|
||||
for (var i = 0; i < lstChildren.length; i++) {
|
||||
if (lstChildren[i].nodeType == 1) // 1 is 'ELEMENT_NODE'
|
||||
{
|
||||
lstChildrenNodes.push(lstChildren[i]);
|
||||
lstChildrenNodes = Utils.getAllDescendant(lstChildren[i], lstChildrenNodes);
|
||||
}
|
||||
}
|
||||
|
||||
return lstChildrenNodes;
|
||||
}
|
||||
|
||||
this._formatTime = function (time, quotationStyle) {
|
||||
if (time < 0) {
|
||||
return '- : -'
|
||||
}
|
||||
var hour = parseInt(time / 3600);
|
||||
var min = parseInt((time - hour * 3600) / 60);
|
||||
var sec = parseInt((time - hour * 3600 - min * 60));
|
||||
var timeText = '';
|
||||
if (hour > 0) {
|
||||
timeText += hour + ':';
|
||||
}
|
||||
if (quotationStyle) {
|
||||
if (min > 0) {
|
||||
timeText += min + "'";
|
||||
}
|
||||
} else {
|
||||
if (min < 10) {
|
||||
timeText += '0' + min + ':';
|
||||
} else {
|
||||
timeText += min + ':';
|
||||
}
|
||||
}
|
||||
if (quotationStyle) {
|
||||
timeText += sec + "''";
|
||||
} else {
|
||||
if (sec < 10) {
|
||||
timeText += '0' + sec;
|
||||
} else {
|
||||
timeText += sec;
|
||||
}
|
||||
}
|
||||
return timeText;
|
||||
}
|
||||
|
||||
this.formatTime = function (time, remains) {
|
||||
var intTime = parseInt(time);
|
||||
var desc = this._formatTime(intTime);
|
||||
if (remains && intTime > 0) {
|
||||
return '-' + desc;
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
|
||||
var Utils = new _Utils;
|
||||
var player = {audio: null, current: null};
|
||||
var videoIframes = [];
|
||||
|
||||
function getContentRoot() {
|
||||
return document.getElementById('editor');
|
||||
}
|
||||
|
||||
function createWRAudio(title, src, duration) {
|
||||
var tmp = document.createElement('div');
|
||||
tmp.innerHTML = WRAUDIO_TEMPLATE;
|
||||
var wrAudio = tmp.firstChild;
|
||||
|
||||
wrAudio.id = 'wraudioid_' + Date.now() + '_' + Math.floor((Math.random() * 1000) + 1);
|
||||
wrAudio.src = src;
|
||||
wrAudio.duration = duration;
|
||||
|
||||
var titleEl = Utils.findChildById(wrAudio, 'wraudio_title', true);
|
||||
titleEl.innerHTML = title || '';
|
||||
var button = Utils.findChildById(wrAudio, 'wraudio_button', true);
|
||||
button.addEventListener('click', function() {
|
||||
clickPlayButton(wrAudio);
|
||||
}, false);
|
||||
|
||||
var elapsedEl = Utils.findChildById(wrAudio, 'wraudio_time_elapsed', true);
|
||||
var remainsEl = Utils.findChildById(wrAudio, 'wraudio_time_remains', true);
|
||||
elapsedEl.innerHTML = Utils.formatTime(0);
|
||||
remainsEl.innerHTML = Utils.formatTime(duration, true);
|
||||
return wrAudio;
|
||||
}
|
||||
|
||||
function resetWRAudio(wrAudio) {
|
||||
if (!wrAudio) {
|
||||
return;
|
||||
}
|
||||
var button = Utils.findChildById(wrAudio, 'wraudio_button', true);
|
||||
button.classList.remove('active');
|
||||
var elapsedEl = Utils.findChildById(wrAudio, 'wraudio_time_elapsed', true);
|
||||
var remainsEl = Utils.findChildById(wrAudio, 'wraudio_time_remains', true);
|
||||
elapsedEl.innerHTML = Utils.formatTime(0);
|
||||
remainsEl.innerHTML = Utils.formatTime(wrAudio.duration, true);
|
||||
|
||||
var progressBar = Utils.findChildById(wrAudio, 'wraudio_progress_bar', true);
|
||||
var progressButton = Utils.findChildById(wrAudio, 'wraudio_progress_button', true);
|
||||
progressBar.style.width = '0';
|
||||
progressButton.style.left = '0';
|
||||
}
|
||||
|
||||
function clickPlayButton(wrAudio) {
|
||||
if (!wrAudio) {
|
||||
return;
|
||||
}
|
||||
var src = wrAudio.src;
|
||||
if (!player.current) {
|
||||
player.current = wrAudio;
|
||||
player.audio.src = src;
|
||||
player.audio.play();
|
||||
} else if (player.current.id != wrAudio.id) {
|
||||
var last = player.current;
|
||||
player.current = wrAudio;
|
||||
resetWRAudio(last);
|
||||
|
||||
player.audio.src = src;
|
||||
player.audio.play();
|
||||
} else {
|
||||
if (!player.audio.paused) {
|
||||
player.audio.pause();
|
||||
} else {
|
||||
player.audio.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pauseCurrentAudio() {
|
||||
if (player.audio) {
|
||||
player.audio.pause();
|
||||
}
|
||||
}
|
||||
|
||||
function pauseCurrentVideo() {
|
||||
let newVideoIframes = [];
|
||||
for (var i = 0; i < videoIframes.length; i++) {
|
||||
var oldFrame = videoIframes[i];
|
||||
oldFrame.src = oldFrame.src;
|
||||
}
|
||||
}
|
||||
|
||||
function pauseMedia() {
|
||||
pauseCurrentAudio();
|
||||
pauseCurrentVideo();
|
||||
}
|
||||
|
||||
function onPlay() {
|
||||
var button = Utils.findChildById(player.current, 'wraudio_button', true);
|
||||
button.classList.add('active');
|
||||
}
|
||||
|
||||
function onPause() {
|
||||
var button = Utils.findChildById(player.current, 'wraudio_button', true);
|
||||
button.classList.remove('active');
|
||||
}
|
||||
|
||||
function onEnded() {
|
||||
var button = Utils.findChildById(player.current, 'wraudio_button', true);
|
||||
button.classList.remove('active');
|
||||
}
|
||||
|
||||
function onDurationchange() {
|
||||
player.current.duration = player.audio.duration;
|
||||
}
|
||||
|
||||
function onVideoPlaying() {
|
||||
pauseCurrentAudio();
|
||||
}
|
||||
|
||||
function onTimeupdate() {
|
||||
var wrAudio = player.current;
|
||||
var elapsedEl = Utils.findChildById(wrAudio, 'wraudio_time_elapsed', true);
|
||||
var remainsEl = Utils.findChildById(wrAudio, 'wraudio_time_remains', true);
|
||||
elapsedEl.innerHTML = Utils.formatTime(player.audio.currentTime);
|
||||
remainsEl.innerHTML = Utils.formatTime(player.audio.duration - player.audio.currentTime, true);
|
||||
|
||||
var progressBar = Utils.findChildById(wrAudio, 'wraudio_progress_bar', true);
|
||||
var progressButton = Utils.findChildById(wrAudio, 'wraudio_progress_button', true);
|
||||
var progress = 0;
|
||||
if (player.audio.duration > 0) {
|
||||
progress = parseInt(player.audio.currentTime / player.audio.duration * 100);
|
||||
}
|
||||
progressBar.style.width = progress + '%';
|
||||
progressButton.style.left = progress + '%';
|
||||
}
|
||||
|
||||
function adjustMPContent() {
|
||||
var imgs = document.querySelectorAll('img[data-src]');
|
||||
for (var i = 0; i < imgs.length ; i++) {
|
||||
var img = imgs[i];
|
||||
img.src = img.getAttribute('data-src');
|
||||
img.style = 'width: auto !important; height: auto !important; visibility: visible !important;max-width:100% !important';
|
||||
}
|
||||
|
||||
var links = document.querySelectorAll('a[href^="https://open.weixin.qq.com/connect/oauth2/authorize"],a[href^="http://open.weixin.qq.com/connect/oauth2/authorize"]');
|
||||
for (var i = 0; i < links.length ; i++) {
|
||||
var link = links[i]
|
||||
|
||||
params = link.href.split('&');
|
||||
for (var j = 0;j<params.length;j++) {
|
||||
param = params[j];
|
||||
if (param.startsWith('redirect_uri=')) {
|
||||
link.href = decodeURIComponent(param.split('redirect_uri=')[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var iframes = document.querySelectorAll('iframe[data-src]');
|
||||
for (var i = 0; i < iframes.length ; i++) {
|
||||
(function (index) {
|
||||
var iframe = iframes[index];
|
||||
var iframeWidth = document.body.offsetWidth;
|
||||
var iframeHeight = iframeWidth * 0.75;
|
||||
var src = iframe.getAttribute('data-src');
|
||||
src = src.replace(/preview.html/, 'player.html');
|
||||
src = src.replace(/&(width=.*height=.*)&/, '&width=' + iframeWidth + '&height=' + iframeHeight + '&');
|
||||
iframe.src = src;
|
||||
// 是否视频
|
||||
/*
|
||||
if ((' ' + iframe.className + ' ').indexOf(' video_iframe ') > -1) {
|
||||
iframe.onload = function () {
|
||||
var styleNode = document.createElement('style');
|
||||
styleNode.type = 'text/css';
|
||||
styleNode.appendChild(document.createTextNode(''));
|
||||
iframe.contentDocument.body.appendChild(styleNode);
|
||||
}
|
||||
}
|
||||
*/
|
||||
// if (iframe.classList.contains("video_iframe")) {
|
||||
// iframe.onload = function() {
|
||||
// if (iframe.contentWindow && iframe.contentWindow.document) {
|
||||
// var videoEl = iframe.contentWindow.document.getElementById("tenvideo_video_player_0");
|
||||
// if (videoEl) {
|
||||
// videoEl.onplaying = function() {
|
||||
// onVideoPlaying();
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
videoIframes.push(iframe);
|
||||
})(i);
|
||||
}
|
||||
|
||||
var votes = document.querySelectorAll(".vote_area");
|
||||
for (var i = 0; i < votes.length; i++) {
|
||||
var vote = votes[i];
|
||||
vote.parentElement.removeChild(vote);
|
||||
}
|
||||
|
||||
var miniPrograms = document.querySelectorAll("mp-miniprogram");
|
||||
for (var i = 0; i < miniPrograms.length; i++) {
|
||||
var miniProgram = miniPrograms[i];
|
||||
var parentElement = miniProgram.parentElement;
|
||||
if (parentElement && parentElement.tagName && parentElement.tagName.toLowerCase() === 'p') {
|
||||
if (parentElement.parentElement) {
|
||||
parentElement.parentElement.removeChild(parentElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var miniAppLinks = document.querySelectorAll(".weapp_text_link, .weapp_image_link");
|
||||
for (var i = 0; i < miniAppLinks.length; i++) {
|
||||
var miniAppLink = miniAppLinks[i];
|
||||
miniAppLink.parentElement.removeChild(miniAppLink);
|
||||
}
|
||||
|
||||
var needPlayer = false;
|
||||
var mpvoices = document.querySelectorAll('mpvoice[voice_encode_fileid]');
|
||||
for (var i = 0; i < mpvoices.length ; i++) {
|
||||
var mpvoice = mpvoices[i];
|
||||
var title = mpvoice.getAttribute('name');
|
||||
var src = 'https://res.wx.qq.com/voice/getvoice?mediaid=' + mpvoice.getAttribute('voice_encode_fileid');
|
||||
var duration = mpvoice.hasAttribute('play_length') ? mpvoice.getAttribute('play_length') / 1000 : -1;
|
||||
var wrAudio = createWRAudio(title, src, duration);
|
||||
mpvoice.parentElement.appendChild(wrAudio);
|
||||
needPlayer = true;
|
||||
}
|
||||
|
||||
var qqmusics = document.querySelectorAll('qqmusic[audiourl]');
|
||||
for (var i = 0; i < qqmusics.length ; i++) {
|
||||
var qqmusic = qqmusics[i];
|
||||
var title = qqmusic.getAttribute('music_name');
|
||||
var src = qqmusic.getAttribute('audiourl');
|
||||
var duration = qqmusic.hasAttribute('play_length') ? qqmusic.getAttribute('play_length') / 1000 : -1;
|
||||
var wrAudio = createWRAudio(title, src, duration);
|
||||
qqmusic.parentElement.appendChild(wrAudio);
|
||||
needPlayer = true;
|
||||
}
|
||||
|
||||
if (needPlayer) {
|
||||
var audio = document.createElement('audio');
|
||||
audio.autoplay = false;
|
||||
audio.controls = true;
|
||||
audio.style.display = "none";
|
||||
|
||||
audio.addEventListener("play", onPlay, false);
|
||||
audio.addEventListener("pause", onPause, false);
|
||||
audio.addEventListener("ended", onEnded, false);
|
||||
audio.addEventListener("durationchange", onDurationchange, false);
|
||||
audio.addEventListener("timeupdate", onTimeupdate, false);
|
||||
|
||||
getContentRoot().appendChild(audio);
|
||||
player.audio = audio;
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
adjustMPContent();
|
||||
}, false);
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
(function() {
|
||||
if (window.wrClickHandler) {
|
||||
window.removeEventListener('click', window.wrClickHandler, true);
|
||||
}
|
||||
window.wrClickHandler = function(e) {
|
||||
console.log('Hook: Click event intercepted', {
|
||||
target: e.target,
|
||||
tagName: e.target.tagName,
|
||||
id: e.target.id,
|
||||
className: e.target.className
|
||||
});
|
||||
|
||||
var eventInfo = null;
|
||||
var currentElement = e.target;
|
||||
while (currentElement && currentElement !== document) {
|
||||
var tagName = currentElement.tagName || '';
|
||||
var currentId = currentElement.id || '';
|
||||
if (currentId === 'js_name') {
|
||||
// 公众号顶部的作者
|
||||
eventInfo = {
|
||||
contentType: 'profile',
|
||||
bizId: ''
|
||||
};
|
||||
console.log('Hook: MP author click intercepted');
|
||||
break;
|
||||
} else if (currentId === 'copyright_info') {
|
||||
// 公众号顶部的文章来源
|
||||
var bizId = typeof source_encode_biz !== 'undefined' ? source_encode_biz : '';
|
||||
if (bizId) {
|
||||
eventInfo = {
|
||||
contentType: 'profile',
|
||||
bizId: bizId
|
||||
};
|
||||
console.log('Hook: Copyright profile click intercepted, bizId:', bizId);
|
||||
}
|
||||
break;
|
||||
} else if (tagName === 'MP-COMMON-PROFILE') {
|
||||
// 公众号
|
||||
var bizId = currentElement.dataset && currentElement.dataset['id'] ? currentElement.dataset['id'] : '';
|
||||
if (bizId) {
|
||||
eventInfo = {
|
||||
contentType: 'profile',
|
||||
bizId: bizId
|
||||
};
|
||||
console.log('Hook: Profile click intercepted, bizId:', bizId);
|
||||
}
|
||||
break;
|
||||
} else if (currentElement.href && currentElement.href.indexOf('mp.weixin.qq.com') !== -1) {
|
||||
// 公众号文章链接
|
||||
eventInfo = {
|
||||
contentType: 'link',
|
||||
url: currentElement.href
|
||||
};
|
||||
console.log('Hook: Link click intercepted, URL:', currentElement.href);
|
||||
break;
|
||||
}
|
||||
currentElement = currentElement.parentElement;
|
||||
}
|
||||
// 如果有 eventInfo,则阻断事件冒泡,拦截公众号原始的跳转弹窗
|
||||
if (eventInfo) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
if (eventInfo && wereadBridge) {
|
||||
wereadBridge.execMPReaderMethod('MPReader', {
|
||||
cmd: 'clickMpInterceptor',
|
||||
data: eventInfo
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
window.addEventListener('click', window.wrClickHandler, true);
|
||||
console.log('Hook: Click interceptor setup completed');
|
||||
})();
|
||||
@@ -1,16 +0,0 @@
|
||||
window.newsInjector = {
|
||||
removeUnnecessary: function() {
|
||||
document.getElementsByTagName('body')[0].style = 'background-color:red'
|
||||
var divs = document.getElementsByTagName('div')
|
||||
for (var i = divs.length - 1; i >= 0; i--) {
|
||||
var node = divs[i]
|
||||
if (node.textContent == '查看全文' &&
|
||||
node.nextSibling &&
|
||||
node.nextSibling.nodeName == 'IMG') {
|
||||
node.style = 'display: none!important;'
|
||||
node.nextSibling.style = 'display: none!important';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# 微信读书 EPUB 阅读器实现架构分析
|
||||
# 读书 EPUB 阅读器实现架构分析
|
||||
|
||||
> 分析日期: 2026-05-18
|
||||
> 基于逆向工程: 微信读书 v10.0.3 (Build 79)
|
||||
> 基于读书 v10.0.3
|
||||
|
||||
---
|
||||
|
||||
## 核心结论:双渲染引擎架构
|
||||
|
||||
微信读书使用了 **两套渲染引擎**,根据内容类型选择不同的渲染路径:
|
||||
读书使用了 **两套渲染引擎**,根据内容类型选择不同的渲染路径:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
@@ -319,7 +319,7 @@ WeRead/Src/Modules/MediaPlatform/
|
||||
```
|
||||
加载优先级 (从低到高):
|
||||
1. default.css - 基础 HTML 标签样式 (Safari 默认)
|
||||
2. replace.css - 微信读书默认替换样式
|
||||
2. replace.css - 读书默认替换样式
|
||||
├── 标题样式 (h1-h6, 使用 Source Han Serif CN 字体)
|
||||
├── 代码块 (pre, 使用 Menlo 字体)
|
||||
├── 图片 (.bodyPic, wr-vertical-center-style: 2)
|
||||
@@ -332,7 +332,7 @@ WeRead/Src/Modules/MediaPlatform/
|
||||
5. 用户设置 - 字号、行高、主题覆盖
|
||||
```
|
||||
|
||||
**自定义 CSS 属性** (微信读书私有):
|
||||
**自定义 CSS 属性** (读书私有):
|
||||
- `wr-vertical-center-style: 1|2` - 图片垂直居中方式
|
||||
- `weread-page-relate: true` - 控制分页时的内容关联
|
||||
|
||||
@@ -380,7 +380,7 @@ WeRead/Src/Modules/MediaPlatform/
|
||||
| `mpForArticle.js` | 文章相关 JS 逻辑 |
|
||||
| `MPExtra.css` | 公众号额外样式 |
|
||||
| `MediaPlatform.css` | 公众号基础样式 |
|
||||
| `WeReadApi.js` | 微信读书 JS API (供 WebView 调用原生功能) |
|
||||
| `WeReadApi.js` | 读书 JS API (供 WebView 调用原生功能) |
|
||||
| `rich_display.js` | 富文本显示逻辑 |
|
||||
| `cssInjector.js` | CSS 注入器 |
|
||||
| `highlight.min.js` | 代码语法高亮 (highlight.js) |
|
||||
@@ -436,7 +436,7 @@ WeRead/Src/Modules/MediaPlatform/
|
||||
├── FZJuZhenXinFang.ttf # 方正聚珍新仿
|
||||
├── Lora-Regular.ttf / Italic.ttf # Lora 衬线体
|
||||
├── PlayfairDisplay-Regular.ttf # Playfair Display
|
||||
├── WeReadLS-Regular/Medium/Bold # 微信读书 LS 系列
|
||||
├── WeReadLS-Regular/Medium/Bold # 读书 LS 系列
|
||||
├── WeReadRN-Regular.ttf # RN 专用字体
|
||||
├── WeRead-Icon.ttf # 图标字体
|
||||
├── WeRead-Rating-Icon.ttf # 评分图标
|
||||
|
||||
+8
-321
@@ -1,323 +1,10 @@
|
||||
# WXRead 剩余问题修复计划
|
||||
|
||||
> 基于 [架构对比分析_WXRead_vs_ReadViewSDK.md](/Users/shen/Work/Code/ReadViewSDK/Doc/架构对比分析_WXRead_vs_ReadViewSDK.md) 的 2026-05-24 核查结果整理。
|
||||
> 目标:把当前已完成的 CoreText 页面直绘继续收口到更接近 WXRead 的页面级排版、命中、装饰和位置模型。
|
||||
|
||||
---
|
||||
|
||||
## 目标概览
|
||||
|
||||
当前已经完成的部分:
|
||||
|
||||
- 文本页已经切到页面级 CoreText 直绘。
|
||||
- 分页器已经切到 `RDEPUBTextLayouter + rd_paginatedFrames`。
|
||||
- 4 级语义断页、`avoidPageBreakInside`、分页缓存、5 层 CSS、`<link>` 外部 CSS 内联都已经落地。
|
||||
|
||||
当前仍需修复的核心问题:
|
||||
|
||||
1. 选区与命中仍依赖 `UITextView` 代理层,没有完全走 CoreText 页面几何。
|
||||
2. 高亮、下划线、搜索命中仍主要靠 attributedString 属性,不是页面级装饰绘制。
|
||||
3. `RDEPUBSelectionOverlayView` 还是半成品,未形成可用闭环。
|
||||
4. inline attachment 虽已大幅收敛,但仍需要对问题书样做专项验书。
|
||||
5. 位置模型仍是 `href + progression`,不是 WXRead 的字符级锚点。
|
||||
6. 章节数据模型仍然分散,没有形成 `WRChapterData` 式内聚模型。
|
||||
|
||||
---
|
||||
|
||||
## 优先级
|
||||
|
||||
### P1:当前渲染链必须补齐
|
||||
|
||||
- CoreText 原生命中测试与选区模型
|
||||
- 页面级装饰绘制层
|
||||
- `RDEPUBSelectionOverlayView` 闭环
|
||||
- inline attachment 验书与细节修正
|
||||
|
||||
### P2:精度与架构内聚
|
||||
|
||||
- 字符级位置模型
|
||||
- 章节数据模型内聚
|
||||
|
||||
### P3:外围能力
|
||||
|
||||
- 翻页 crash patch
|
||||
- 多栏
|
||||
- 字体系统
|
||||
- TTS / DRM / Pencil 等
|
||||
|
||||
---
|
||||
|
||||
## Phase 1:CoreText 命中与选区收口
|
||||
|
||||
### 目标
|
||||
|
||||
去掉文本页对 `UITextView` 选区代理的核心依赖,改成和 WXRead 一样基于页面 `layoutFrame` / `line` / `run` 的命中测试与选区计算。
|
||||
|
||||
### 范围
|
||||
|
||||
- `RDEPUBTextContentView`
|
||||
- `RDEPUBSelectionOverlayView`
|
||||
- 新增页面几何辅助对象或工具类型
|
||||
|
||||
### 主要工作
|
||||
|
||||
1. 基于当前页 `DTCoreTextLayoutFrame` 建立字符命中入口。
|
||||
2. 实现单点命中:`CGPoint -> character index`。
|
||||
3. 实现拖拽扩选:起点 index、终点 index、range 归一化。
|
||||
4. 输出选区矩形集合:用于高亮、手柄、菜单定位。
|
||||
5. 让 `RDEPUBSelectionOverlayView` 直接消费 CoreText 选区矩形,而不是依赖 `UITextView`。
|
||||
6. 保留 `UITextView` 仅作为兜底或临时兼容层,最终目标是让它不再决定正文选区几何。
|
||||
|
||||
### 产出
|
||||
|
||||
- 页面级命中测试工具
|
||||
- 页面级选区 range 计算逻辑
|
||||
- 页面级选区 rect 计算逻辑
|
||||
- `RDEPUBSelectionOverlayView` 可实际工作
|
||||
|
||||
### 验收标准
|
||||
|
||||
- 长按正文任意位置,选区起点与落点稳定。
|
||||
- 拖动选区不会出现明显错位、跳行、截断。
|
||||
- 脚注图标、inline image、跨行高亮附近仍能稳定选中相邻文字。
|
||||
- 不再依赖 `UITextView.selectedRange` 作为主选区真值。
|
||||
|
||||
### 风险
|
||||
|
||||
- DTCoreText 暴露的行/run 几何可能和系统 `UITextView` 行为不完全一致。
|
||||
- inline attachment 附近的 index 映射需要特别小心。
|
||||
|
||||
---
|
||||
|
||||
## Phase 2:页面级装饰绘制层
|
||||
|
||||
### 目标
|
||||
|
||||
把高亮、下划线、搜索命中从 attributedString 注入迁到页面级 CGContext 绘制,和 WXRead 的页面装饰层一致。
|
||||
|
||||
### 范围
|
||||
|
||||
- `RDEPUBDirectCoreTextPageView`
|
||||
- `RDEPUBTextContentView`
|
||||
- 可能新增 `RDEPUBPageDecorationRenderer`
|
||||
|
||||
### 主要工作
|
||||
|
||||
1. 将高亮、下划线、搜索命中从“内容属性”改成“页面装饰数据”。
|
||||
2. 基于当前页 range 计算每种装饰的 rect 集合。
|
||||
3. 在页面 `draw(_:)` 或 overlay 中按层次绘制:
|
||||
- 搜索命中
|
||||
- 高亮背景
|
||||
- 下划线
|
||||
- 选区高亮
|
||||
4. 统一颜色、透明度、绘制顺序,避免正文颜色污染。
|
||||
|
||||
### 产出
|
||||
|
||||
- 页面装饰几何模型
|
||||
- 页面装饰绘制实现
|
||||
- attributedString 注入逻辑的瘦身或移除
|
||||
|
||||
### 验收标准
|
||||
|
||||
- 搜索高亮不再改变底层排版结果。
|
||||
- 高亮和下划线不再依赖正文属性重排。
|
||||
- 同一页同时存在搜索命中、标注、选区时,层次稳定、不互相遮挡异常。
|
||||
|
||||
### 风险
|
||||
|
||||
- 需要处理跨行 range 拆分。
|
||||
- 需要处理 attachment、段前段后间距、最后一行空白的 rect 裁剪。
|
||||
|
||||
---
|
||||
|
||||
## Phase 3:inline attachment 专项验书与收敛
|
||||
|
||||
### 目标
|
||||
|
||||
继续把 inline image、脚注图标、特殊图片在排版指标和页面显示上收紧到更接近 WXRead。
|
||||
|
||||
### 范围
|
||||
|
||||
- `RDEPUBTextRendererSupport.normalizeAttachmentLayoutForWXRead`
|
||||
- `RDEPUBTextContentView.normalizeInlineAttachments`
|
||||
- 问题 EPUB 样本专项验证
|
||||
|
||||
### 主要工作
|
||||
|
||||
1. 建立 attachment 分类表:
|
||||
- 脚注图标
|
||||
- 普通 inline image
|
||||
- block image
|
||||
- cover image
|
||||
2. 逐类校正:
|
||||
- `displaySize`
|
||||
- `verticalAlignment`
|
||||
- 基线占位
|
||||
- 页内/跨页表现
|
||||
3. 对问题书样做页对页比对,记录剩余差异。
|
||||
4. 去掉重复、冲突的 attachment 二次归一化逻辑,保证“同一类 attachment 只走一套规则”。
|
||||
|
||||
### 产出
|
||||
|
||||
- attachment 规则矩阵
|
||||
- 书样问题清单
|
||||
- 附件归一化逻辑收口
|
||||
|
||||
### 验收标准
|
||||
|
||||
- `.qqreader-footnote` 与正文基线对齐稳定。
|
||||
- 普通内容图片不再出现页内漂移、倒置、重复绘制、页尾大空白。
|
||||
- 同一 EPUB 在分页和显示阶段不会再出现 attachment 指标不一致。
|
||||
|
||||
### 风险
|
||||
|
||||
- 不同 EPUB 资源命名习惯不同,分类可能需要结合 class、src、style 多维判断。
|
||||
|
||||
---
|
||||
|
||||
## Phase 4:字符级位置模型
|
||||
|
||||
### 目标
|
||||
|
||||
从当前 `href + progression` 提升到更接近 WXRead 的字符级位置锚点,减少重排后的标注漂移。
|
||||
|
||||
### 范围
|
||||
|
||||
- `RDEPUBLocation`
|
||||
- `RDEPUBTextBook`
|
||||
- `RDEPUBHighlight` / `RDEPUBBookmark`
|
||||
- 选区序列化与恢复
|
||||
|
||||
### 主要工作
|
||||
|
||||
1. 设计新的文本位置载体:
|
||||
- 章节索引
|
||||
- 章节内字符偏移
|
||||
- 可选 fragment / 行列辅助信息
|
||||
2. 建立双向转换:
|
||||
- 页面 → 字符 range
|
||||
- 字符 range → 页面
|
||||
3. 保留对旧 `progression` 数据的兼容读取。
|
||||
4. 更新标注恢复、搜索跳转、书签跳转逻辑。
|
||||
|
||||
### 产出
|
||||
|
||||
- 新位置模型
|
||||
- 兼容迁移策略
|
||||
- 标注恢复精度提升
|
||||
|
||||
### 验收标准
|
||||
|
||||
- 改字号、改行距后重新分页,已有标注仍能落回正确正文范围。
|
||||
- 多次翻页、重建 `RDEPUBTextBook` 后,范围恢复稳定。
|
||||
|
||||
### 风险
|
||||
|
||||
- 旧数据兼容与迁移策略需要谨慎。
|
||||
- attachment、fragment marker、语义 marker 会影响纯字符 offset 映射。
|
||||
|
||||
---
|
||||
|
||||
## Phase 5:章节数据模型内聚
|
||||
|
||||
### 目标
|
||||
|
||||
把分散在 `RDEPUBTextChapter`、`RDEPUBHighlight`、目录、分页诊断里的数据逐步收敛成更像 `WRChapterData` 的一体化章节模型。
|
||||
|
||||
### 范围
|
||||
|
||||
- `RDEPUBTextChapter`
|
||||
- `RDEPUBTextBook`
|
||||
- 标注、目录、分页诊断承载方式
|
||||
|
||||
### 主要工作
|
||||
|
||||
1. 定义章节聚合对象,统一承载:
|
||||
- attributedContent
|
||||
- page frames / page ranges
|
||||
- fragment offsets
|
||||
- highlights / bookmarks
|
||||
- outline / TOC mapping
|
||||
- pagination diagnostics
|
||||
2. 让控制器层从“自己拼数据”改成“消费章节聚合对象”。
|
||||
3. 为后续位置模型、装饰绘制、选区恢复提供统一数据入口。
|
||||
|
||||
### 产出
|
||||
|
||||
- 新章节聚合模型
|
||||
- 控制器简化
|
||||
- 数据流收口
|
||||
|
||||
### 验收标准
|
||||
|
||||
- 阅读器控制器中的章节相关派生逻辑明显减少。
|
||||
- 标注、分页、目录不再分散在多个不一致的数据结构里。
|
||||
|
||||
---
|
||||
|
||||
## Phase 6:外围能力补齐
|
||||
|
||||
### 目标
|
||||
|
||||
处理不影响当前正文质量但和 WXRead 存在差距的外围能力。
|
||||
|
||||
### 包含项
|
||||
|
||||
- `UIPageViewController` crash patch
|
||||
- 多栏排版
|
||||
- 字体动态加载
|
||||
- TTS / DRM / Pencil / 预加载
|
||||
|
||||
### 建议
|
||||
|
||||
- 这批能力不要和当前正文排版修复混在一个阶段。
|
||||
- 先把正文页面链路闭环,再独立立项。
|
||||
|
||||
---
|
||||
|
||||
## 执行顺序建议
|
||||
|
||||
1. Phase 1:CoreText 命中与选区收口
|
||||
2. Phase 2:页面级装饰绘制层
|
||||
3. Phase 3:inline attachment 专项验书与收敛
|
||||
4. Phase 4:字符级位置模型
|
||||
5. Phase 5:章节数据模型内聚
|
||||
6. Phase 6:外围能力补齐
|
||||
|
||||
原因:
|
||||
|
||||
- Phase 1 和 Phase 2 会直接决定“页面几何模型”是否真正闭环。
|
||||
- Phase 3 需要建立在前两步的页面几何一致性之上,否则 attachment 还会被多套逻辑干扰。
|
||||
- Phase 4 和 Phase 5 更偏位置与架构内聚,适合在页面几何稳定后推进。
|
||||
|
||||
---
|
||||
|
||||
## 每阶段验证方式
|
||||
|
||||
### 功能验证
|
||||
|
||||
- 问题 EPUB 书样页对页比对
|
||||
- 翻页前后位置恢复
|
||||
- 高亮、搜索、选区、脚注图标组合场景验证
|
||||
|
||||
### 构建验证
|
||||
|
||||
- `RDReaderView` scheme 构建
|
||||
- `ReadViewDemo` scheme 构建
|
||||
|
||||
### 回归关注点
|
||||
|
||||
- 页尾空白是否回归
|
||||
- 图片是否重复绘制
|
||||
- 内容是否倒置
|
||||
- 搜索高亮是否影响分页
|
||||
- 标注恢复是否漂移
|
||||
|
||||
---
|
||||
|
||||
## 建议的文档联动
|
||||
|
||||
- 本文档用于排期和实施顺序。
|
||||
- [架构对比分析_WXRead_vs_ReadViewSDK.md](/Users/shen/Work/Code/ReadViewSDK/Doc/架构对比分析_WXRead_vs_ReadViewSDK.md) 继续作为“差距现状”文档。
|
||||
- 每完成一个阶段,回写一份“已完成/剩余风险”更新到对比分析文档中。
|
||||
> 本文档内容已整合进入 [架构对比分析_WXRead_vs_ReadViewSDK.md](/Users/shen/Work/Code/ReadViewSDK/Doc/架构对比分析_WXRead_vs_ReadViewSDK.md)。
|
||||
> 后续请只维护对比文档中的以下部分:
|
||||
>
|
||||
> - `核查摘要`
|
||||
> - `缺失能力清单`
|
||||
> - `收口路线(整合原修复计划)`
|
||||
|
||||
保留本文件仅为兼容旧引用,避免计划与现状继续出现双份口径。
|
||||
|
||||
+2
-11
@@ -13,7 +13,7 @@
|
||||
| 文档 | 对应模块 | 说明 |
|
||||
|------|----------|------|
|
||||
| [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/`(6 文件) | DTCoreText HTML→NSAttributedString 渲染管线、片段标记注入/提取、CoreText 字符范围分页算法、Location↔PageNumber 双向转换、全文搜索引擎 |
|
||||
| [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 全生命周期、三条渲染路径分发、配置变更检测与响应、工具栏/目录/高亮/搜索/设置面板交互流、阅读位置持久化、主题管理 |
|
||||
|
||||
@@ -21,16 +21,7 @@
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [TopToolView接管导航栏.md](FeatureSolution/TopToolView接管导航栏.md) | TopToolView 接管系统导航栏 + 工具栏 Auto Layout 布局修复方案 |
|
||||
| [RDEPUBReaderTopToolView返回按钮误隐藏工具栏问题讨论.md](FeatureSolution/RDEPUBReaderTopToolView返回按钮误隐藏工具栏问题讨论.md) | 返回按钮点击被误判为阅读区域点击,导致工具栏隐藏的问题分析与修复建议 |
|
||||
| [增加批注功能方案讨论.md](FeatureSolution/增加批注功能方案讨论.md) | 基于现有高亮能力演进出完整批注创建、管理与定位链路的方案讨论 |
|
||||
| [标注功能两套渲染链路实施方案.md](FeatureSolution/标注功能两套渲染链路实施方案.md) | 拆解 WebView 与 DTCoreText 两条正文链路如何统一实现高亮、划线与批注能力 |
|
||||
| [文本选中菜单定制方案讨论.md](FeatureSolution/文本选中菜单定制方案讨论.md) | 基于当前 UITextView 选区实现,定制“拷贝 / 高亮 / 批注”菜单并移除系统无关项的方案讨论 |
|
||||
| [批注和高亮功能详细需求文档.md](FeatureSolution/批注和高亮功能详细需求文档.md) | 整合标注链路与文本菜单定制后的完整需求文档,统一定义高亮、划线、批注与菜单行为 |
|
||||
| [横竖屏切换支持方案讨论.md](FeatureSolution/横竖屏切换支持方案讨论.md) | 讨论 EPUB 阅读器横竖屏切换后的重分页、阅读位置恢复、双页布局与视口变化统一处理方案 |
|
||||
| [自动化测试方案讨论.md](FeatureSolution/自动化测试方案讨论.md) | 讨论 EPUB 阅读器自动化测试体系的分层建设方式,覆盖测试 target、样书夹具、XCTest 与 XCUITest 的实施边界 |
|
||||
| [样书基线验证方案讨论.md](FeatureSolution/样书基线验证方案讨论.md) | 讨论四本固定 EPUB 样书的基线验证机制,包含样书矩阵、验证流程和可复用的基线表模板 |
|
||||
| [书签能力方案讨论.md](FeatureSolution/书签能力方案讨论.md) | 讨论 EPUB 阅读器轻量书签能力的设计方案,覆盖独立模型、持久化、入口 UI、列表管理与位置恢复策略 |
|
||||
| [ReflowableEPUB_WXReadRenderer_Design.md](FeatureSolution/ReflowableEPUB_WXReadRenderer_Design.md) | 基于读书反编译的 CoreText 渲染架构,设计 Reflowable EPUB 的增强文本渲染方案(CSS 分层、类型器升级) |
|
||||
|
||||
## 目录约定
|
||||
|
||||
|
||||
+183
-152
@@ -1,249 +1,280 @@
|
||||
# 架构对比分析:微信读书 vs ReadViewSDK
|
||||
# 架构对比分析:读书 vs ReadViewSDK
|
||||
|
||||
> 基于微信读书 v10.0.3 (Build 79) 逆向工程,对比当前 ReadViewSDK 项目架构差距。
|
||||
> 分析日期: 2026-05-23
|
||||
> 当前代码核查日期: 2026-05-24
|
||||
> 参考文档: `Doc/WXRead/decompiled-doc.md`、`Doc/WXRead/resources-doc.md`、`Doc/WXRead/微信读书EPUB阅读器实现架构.md`
|
||||
> 基于读书 v10.0.3 (Build 79) 逆向文档,与当前 ReadViewSDK 代码在 2026-05-24 的核查结果整合。
|
||||
> 本文档已吸收原 [WXRead剩余问题修复计划.md](/Users/shen/Work/Code/ReadViewSDK/Doc/WXRead剩余问题修复计划.md) 的阶段方案,后续以本文档作为单一真值。
|
||||
|
||||
> 标注说明:
|
||||
> - `✅ 已实现`:当前代码已经按接近 WXRead 的路径落地
|
||||
> - `⚠️ 有差异/有问题`:已经实现一部分,但仍与 WXRead 有结构差异,或者当前实现仍有已知问题
|
||||
> - `⚠️ 有差异/有问题`:已经实现一部分,但仍与 WXRead 有结构差异,或仍有已知问题
|
||||
> - `❌ 未实现`:当前代码中仍缺失
|
||||
|
||||
---
|
||||
|
||||
## 核心结论
|
||||
|
||||
微信读书使用**双渲染引擎架构**(Path A: 原生 CoreText / Path B: WebView)。ReadViewSDK 当前也已经是 CoreText 文本页 + WebView 双路径,但文本页仍保留 `UITextView` 作为选区代理层,因此“页面绘制”已接近 WXRead,“选区/装饰/命中测试”仍未完全收口到同一套 CoreText 页面模型。
|
||||
ReadViewSDK 当前已经不是“旧 UITextView 阅读器”了,文本主链路已经收口到:
|
||||
|
||||
- CoreText 页面直绘
|
||||
- `RDEPUBTextLayouter` 分页
|
||||
- 5 层 CSS 级联
|
||||
- `<link>` 样式表内联
|
||||
- 页面级 hit test / 选区 / 高亮 / 批注
|
||||
- 字符锚点与 `fileIndex/row/column` 语义的初步位置模型
|
||||
|
||||
和 WXRead 现在的主要差距,已经从“能不能工作”收缩成“是否完全同构”:
|
||||
|
||||
1. 页面几何模型还没完全等价 `WRCoreTextLayoutFrame`
|
||||
2. 章节数据模型还没完全收口成 `WRChapterData`
|
||||
3. 位置模型已进入字符级锚点阶段,但还不是完整 `WREpubPositionConverter`
|
||||
4. 翻页稳定性、预加载、预测翻页、显示切换一致性已经接入主链路,但还没完全补齐到 WXRead 同构
|
||||
|
||||
---
|
||||
|
||||
## 核查摘要
|
||||
|
||||
### ✅ 已按 WXRead 路径落地
|
||||
### ✅ 已实现
|
||||
|
||||
- 文本页已经改成页面级 CoreText 直绘,不再是单纯 `UITextView` 展示。
|
||||
- `RDEPUBTextLayouter` 已经接入 `RDEPUBTextBookBuilder` / `RDPlainTextBookBuilder`。
|
||||
- 文本页已经改成页面级 CoreText 直绘。
|
||||
- `RDEPUBTextLayouter` 已接入 `RDEPUBTextBookBuilder` / `RDPlainTextBookBuilder`。
|
||||
- 4 级语义断页、`avoidPageBreakInside` 页尾回退、分页缓存都已经落地。
|
||||
- 5 层 CSS 级联和 EPUB `<link>` 外部样式表内联都已经落地。
|
||||
- CoreText 页面 hit test、长按选区、菜单锚点、复制/高亮/批注主链路已经接入页面几何。
|
||||
- CoreText 路径的高亮、注释、搜索命中已经走页面装饰层,而不是继续改正文布局真值。
|
||||
- WebView 路径的高亮/批注展示链路已经补齐,不再因为 DOM 包裹失败而“有记录但不着色”。
|
||||
- 分页器已经补上 inline footnote attachment 不整段挪页、标题 `keepWithNext`、`weread-page-relate` 页首借行这几类 WXRead 风格规则。
|
||||
- 章节尾部“仅空白/段落分隔符”的尾页丢弃,以及极短尾页回并已经落地,`宝山辽墓材料与释读` 第 31 页空白问题已修复。
|
||||
|
||||
### ❌ 仍未实现
|
||||
### ⚠️ 有差异/有问题
|
||||
|
||||
- CoreText 原生 hit test / 选区命中。
|
||||
- 独立 CGContext 标注绘制层与搜索高亮层。
|
||||
- WXRead 式字符级位置模型(`fileIndex + row + column`)。
|
||||
- `WRChapterData` 一体化章节数据模型。
|
||||
- UIPageViewController crash patch、多栏、字体动态加载、TTS/DRM 等外围能力。
|
||||
- `RDEPUBPageLayoutSnapshot`、overlay、decoration 的对象边界还没完全等价 `WRCoreTextLayoutFrame`。
|
||||
- `RDEPUBSelectionOverlayView.absoluteRange(at:)` 一类回查能力仍不完整,页面几何闭环还差最后一段。
|
||||
- 位置模型虽然已经有 `rangeAnchor` 和 `fileIndex/row/column` 语义,但还不是完整的 WXRead 双向位置转换器。
|
||||
- `RDEPUBChapterData` 已承担页内高亮/搜索/页码查询职责,但还没完全吸收 controller/book 层的章节语义。
|
||||
- `RDReaderView` 已有 `pageCurl` fault patch、预加载、预测翻页和显示切换位置恢复,但还不是 WXRead 那种完整宿主层补丁集。
|
||||
- `default.css` / `replace.css` / `dark.css` 的效果已接近,但资源体系和 WXRead 私有 CSS 仍不是一比一镜像。
|
||||
|
||||
### ⚠️ 已实现但仍有问题/差异
|
||||
### ❌ 未实现
|
||||
|
||||
- 当前文本页虽然已直绘,但仍依赖 `UITextView` 代理选区,页面几何模型没有完全统一。
|
||||
- `RDEPUBSelectionOverlayView` 仍是半成品,尚未形成完整可用的 CoreText 选区/装饰 overlay。
|
||||
- 高亮、下划线、搜索命中仍主要靠 attributedString 属性注入,而不是 WXRead 那种页面装饰绘制。
|
||||
- inline attachment 已按 WXRead 路径收敛,但仍需要继续结合问题书样验证特殊图片、脚注图标和页内基线细节。
|
||||
- 完整 `WRChapterData` 同构模型
|
||||
- 完整 `WREpubPositionConverter` 同构模型
|
||||
- 预加载、预测翻页、显示切换一致性的完整宿主层方案
|
||||
- 多栏、字体动态加载、繁简转换
|
||||
- TTS / DRM / Pencil
|
||||
|
||||
---
|
||||
|
||||
## 1. 渲染路径对比
|
||||
|
||||
| 维度 | 微信读书 | ReadViewSDK | 核查 |
|
||||
| 维度 | 读书 | ReadViewSDK | 核查 |
|
||||
|------|---------|-------------|------|
|
||||
| EPUB 文本渲染 | `WRPageView.drawRect:` → `CTFrameDraw` 到 CGContext | `RDEPUBTextContentView` → `RDEPUBDirectCoreTextPageView.draw(_:)` + `DTCoreTextLayoutFrame.draw(in:)` 页面级直绘 | ✅ 已实现 |
|
||||
| WebView 路径 | WKWebView(公众号/文集文章) | WKWebView(interactive/fixed-layout EPUB) | ✅ 已实现 |
|
||||
| 文本选区 | `CTLineGetStringIndexForPosition` 坐标级 hit test | 仍主要依赖 `UITextView` 代理选区;`RDEPUBSelectionOverlayView` 仍是半成品 | ⚠️ 有差异/有问题 |
|
||||
| 标注绘制 | `CTFrame` 层叠加绘制(CGContext) | 高亮/下划线仍主要通过 attributedString 属性注入,不是独立 CGContext 装饰层 | ⚠️ 有差异/有问题 |
|
||||
| 搜索高亮 | `WRCoreTextLayoutFrame.highlightSearchResults:` 直接绘制 | 仍是 attributedString 注入临时高亮属性 | ⚠️ 有差异/有问题 |
|
||||
| 文本选区 | `CTLineGetStringIndexForPosition` 坐标级 hit test | `RDEPUBPageInteractionController` + `RDEPUBSelectionOverlayView` 已接入主链路 | ✅ 已实现 |
|
||||
| 标注绘制 | `CTFrame` 层叠加绘制(CGContext) | CoreText 路径已走页面装饰层;WebView 路径仍是 JS/DOM 高亮 | ⚠️ 有差异/有问题 |
|
||||
| 搜索高亮 | `WRCoreTextLayoutFrame.highlightSearchResults:` 直接绘制 | CoreText 路径已页面化;不同渲染路径仍不是同一套对象模型 | ⚠️ 有差异/有问题 |
|
||||
|
||||
**核查结论:** 这段原分析已经过时。当前文本页的底层排版和绘制已经切到 CoreText 页面直绘,但“命中测试、标注装饰层、搜索高亮层”还没有完全像 WXRead 那样归并到同一套页面级 CoreText 几何模型。
|
||||
**结论:** 文本页的“是否页面级 CoreText”已经不是问题,当前差距是页面装饰对象和双路径一致性。
|
||||
|
||||
---
|
||||
|
||||
## 2. 分页引擎对比
|
||||
|
||||
| 维度 | 微信读书 | ReadViewSDK | 核查 |
|
||||
| 维度 | 读书 | ReadViewSDK | 核查 |
|
||||
|------|---------|-------------|------|
|
||||
| 分页核心 | `WRCoreTextLayouter` + `WRCoreTextLayoutFrame` | `RDEPUBTextLayouter` + `RDEPUBTextLayoutFrame`,并已接入 `RDEPUBTextBookBuilder` / `RDPlainTextBookBuilder` | ✅ 已实现 |
|
||||
| 断页策略 | 4 级语义断页:语义边界 > 附件边界 > 块边界 > 帧限制 | `rd_paginatedFrames` 已启用 4 级语义断页,并改为与显示层一致的 `DTCoreTextLayoutFrame.visibleStringRange` | ✅ 已实现 |
|
||||
| 分页配置 | `WRCoreTextLayoutConfig`(列数、栏间距、孤行控制、连字符) | 已有 `RDEPUBTextLayoutConfig`,含 `avoidOrphans`/`avoidWidows`/`avoidPageBreakInsideEnabled`/`imageMaxHeightRatio`,但多栏等能力仍缺 | ⚠️ 有差异/有问题 |
|
||||
| 缓存 | `WRChapterPageCount.currentCacheKeyWithBookId:` 按排版设置缓存 | 已有 `RDEPUBTextBookCache`,按书籍 + 排版设置缓存每章分页结果 | ✅ 已实现 |
|
||||
| 跨页避让 | `avoidPageBreakInsideByRemovingLastLinesIfNeeded` — CSS `avoid-page-break-inside` 实现 | 已实现页尾最多回退 3 行的 WXRead 式 `avoidPageBreakInside` 处理 | ✅ 已实现 |
|
||||
| 分页核心 | `WRCoreTextLayouter` + `WRCoreTextLayoutFrame` | `RDEPUBTextLayouter` + `RDEPUBTextLayoutFrame` | ✅ 已实现 |
|
||||
| 断页策略 | 4 级语义断页 | 已启用 4 级语义断页 | ✅ 已实现 |
|
||||
| `avoidPageBreakInside` | 页尾最多回退若干行避免破碎 | 已实现页尾最多回退 3 行 | ✅ 已实现 |
|
||||
| inline footnote | 行内脚注不应触发整段挪页 | 已修正为仅块级 attachment 参与整块挪页 | ✅ 已实现 |
|
||||
| 标题 keep-with-next | 标题不能孤悬页尾 | 已补 `keepWithNext` 语义与页尾回退 | ✅ 已实现 |
|
||||
| `weread-page-relate` | 页首关联块需要借上一页一行 | 已补页首 `pageRelate` 借行规则 | ✅ 已实现 |
|
||||
| 章节尾页收口 | 丢弃空白尾页、合并极短尾页 | 已补尾页空白丢弃与超短尾页回并 | ✅ 已实现 |
|
||||
| 分页配置 | `WRCoreTextLayoutConfig`(含多栏等) | 已有 `RDEPUBTextLayoutConfig`,但多栏等仍缺 | ⚠️ 有差异/有问题 |
|
||||
| 缓存 | 按书籍 + 排版设置缓存 | 已有 `RDEPUBTextBookCache` 磁盘缓存 | ✅ 已实现 |
|
||||
|
||||
**核查结论:** 这段原判断已经过时。`RDEPUBTextBookBuilder` 和 `RDPlainTextBookBuilder` 已经走 `rd_paginatedFrames(size:fragmentOffsets:)`,不再是“能力已写但未接入”的状态。
|
||||
**结论:** 当前分页质量问题已经进入问题书细抠阶段,不再是“缺分页器”的阶段;章节尾部空白页这类系统性问题也已经开始按 WXRead 规则收口。
|
||||
|
||||
---
|
||||
|
||||
## 3. CSS 处理对比
|
||||
|
||||
| 维度 | 微信读书 | ReadViewSDK | 核查 |
|
||||
| 维度 | 读书 | ReadViewSDK | 核查 |
|
||||
|------|---------|-------------|------|
|
||||
| CSS 级联层级 | 5 级:`default.css < replace.css < dark.css < EPUB 内嵌 < 用户设置` | `RDEPUBTextStyleSheetPackage` 已实际生成 5 层:`default/replace/dark/epub/user`,并分别注入 | ✅ 已实现 |
|
||||
| EPUB `<link>` CSS | `DTHTMLAttributedStringBuilder` 解析前级联合并 `<link>` 引用的 CSS | 已实现 `<link rel="stylesheet">` 抽取、内联、URL 重写与资源诊断 | ✅ 已实现 |
|
||||
| 自定义 CSS 属性 | `wr-vertical-center-style`(图片垂直居中)、`weread-page-relate`(跨页关联)、`DTPageBreakInsideAvoid`(断页避让) | 已部分支持:`pageRelate`、`avoidPageBreakInside`、attachment placement 推断;但并非完整 WeRead 属性集 | ⚠️ 有差异/有问题 |
|
||||
| CSS 层级模型 | 已定义 `RDEPUBTextStyleSheetPackage`/`RDEPUBTextStyleSheetLayer`(5 种 kind),但未完整使用 | 已实际使用;但和 WXRead 资源体系仍不是同一批 CSS 文件/规则 | ⚠️ 有差异/有问题 |
|
||||
| CSS 级联层级 | 5 级:`default < replace < dark < epub < user` | `RDEPUBTextStyleSheetPackage` 已实际生成并注入 5 层 | ✅ 已实现 |
|
||||
| EPUB `<link>` CSS | 解析前级联合并外部样式 | 已实现抽取、内联、URL 重写与诊断 | ✅ 已实现 |
|
||||
| 自定义 CSS 属性 | `wr-vertical-center-style`、`weread-page-relate`、断页相关私有属性 | 已支持 `pageRelate`、`avoidPageBreakInside`、attachment placement;仍非完整 WeRead 属性全集 | ⚠️ 有差异/有问题 |
|
||||
| WXRead 私有 CSS 文件 | 使用 WeRead 自带 `default/replace/dark/...` | 当前是等价策略,不是原文件原样镜像 | ⚠️ 有差异/有问题 |
|
||||
| `replaceForLatinLanguageBook.css` | 拉丁语言专项字体规则 | 无对应 | ❌ 未实现 |
|
||||
|
||||
**核查结论:** “未处理 `<link>` 外部 CSS” 这一条已经不成立;但外部 CSS 即使已被内联,规则兼容度和 WXRead 自带 replace/default 体系仍可能存在差异。
|
||||
**结论:** CSS 主机制已对齐,剩下是规则细节和私有资源体系差异。
|
||||
|
||||
---
|
||||
|
||||
## 4. 位置模型对比
|
||||
|
||||
| 维度 | 微信读书 | ReadViewSDK |
|
||||
|------|---------|-------------|
|
||||
| 位置模型 | `WREpubPositionConverter` — `(fileIndex, row, column)` 三元组 | `RDEPUBLocation` — `href + progression` 浮点值 |
|
||||
| 精度 | 字符级精度 | 章节内比例精度 (0..1) |
|
||||
| 索引构建 | `initIndices` 预建全书字符索引表 | 无索引,每次实时计算 |
|
||||
| 双向转换 | `stringRangeFromFileRange:` / `fileIndexForCharacterPosition:` / `localOffsetInFileAtIndex:forGlobalPosition:` | `RDEPUBTextBook.pageNumber(for:)` / `location(forPageNumber:)` |
|
||||
| 维度 | 读书 | ReadViewSDK | 核查 |
|
||||
|------|---------|-------------|------|
|
||||
| 主模型 | `WREpubPositionConverter` `(fileIndex, row, column)` | `RDEPUBTextAnchor` + `rangeAnchor` + `RDEPUBTextIndexTable` | ⚠️ 有差异/有问题 |
|
||||
| 恢复精度 | 字符级双向恢复 | 已优先按 anchor 恢复,弱化 `progression` | ⚠️ 有差异/有问题 |
|
||||
| 旧数据兼容 | 兼容历史位置模型 | 已兼容旧 `spineIndex` 等旧字段解码 | ✅ 已实现 |
|
||||
| 全量双向转换 | 文件位置 <-> 全书字符位置 <-> 页码 | 已有一部分,但还不是完整 `WREpubPositionConverter` | ⚠️ 有差异/有问题 |
|
||||
|
||||
**影响分析:** progression 是 0..1 浮点值,在跨字号/行距重排后能保持大致位置,但无法精确到字符。微信读书的三元组提供字符级精度,适合精确标注恢复。当前 progression 模式对大部分场景够用,但在密集标注场景下可能出现标注漂移。
|
||||
|
||||
**核查:** `❌ 未实现`。当前位置模型仍然不是 WXRead 的字符级三元组。
|
||||
**结论:** 这一块已经从 `❌` 进入 `⚠️`,但还没有达到“WXRead 同构”。
|
||||
|
||||
---
|
||||
|
||||
## 5. 章节数据模型对比
|
||||
|
||||
| 维度 | 微信读书 | ReadViewSDK |
|
||||
|------|---------|-------------|
|
||||
| 核心模型 | `WRChapterData` — 一个对象承载渲染结果 + 标注 + 目录 + 免费试读截断 | `RDEPUBTextChapter` + `RDEPUBHighlight` + `EPUBChapterInfo` 分散在多处 |
|
||||
| 标注内聚 | `addHighlightInRange:` / `addUnderLineToAttributedString:` 直接在 chapterData 上操作 | 标注在 `RDEPUBReaderController` 层管理,与章节数据分离 |
|
||||
| 目录生成 | `WRChapterData.generateOutlineContents` — 从渲染结果反向提取目录 | 目录从 NCX/Nav 独立解析,不与渲染数据关联 |
|
||||
| 维度 | 读书 | ReadViewSDK | 核查 |
|
||||
|------|---------|-------------|------|
|
||||
| 核心模型 | `WRChapterData` 一体化承载渲染结果 + 标注 + 搜索 + 目录 | `RDEPUBChapterData` 已存在,但职责仍未完全收拢 | ⚠️ 有差异/有问题 |
|
||||
| 页内高亮查询 | 章节对象直接提供 | `RDEPUBChapterData.highlights(on:from:)` 已提供 | ✅ 已实现 |
|
||||
| 页内搜索查询 | 章节对象直接提供 | `RDEPUBChapterData.searchResults(on:from:)` 已提供 | ✅ 已实现 |
|
||||
| 页码/位置查询 | 章节对象内聚 | 已部分迁入 `RDEPUBChapterData`,仍有一部分散在 controller/book | ⚠️ 有差异/有问题 |
|
||||
| 目录与章节语义 | 基于章节数据统一管理 | 仍大量依赖独立 TOC 解析与 controller 调度 | ⚠️ 有差异/有问题 |
|
||||
|
||||
**核查:** `❌ 未实现`。章节数据模型仍未收敛成 WXRead 的 `WRChapterData` 一体化模式。
|
||||
**结论:** 章节对象已经不是空白,但也还没收口成 WXRead 那种唯一章节语义中心。
|
||||
|
||||
---
|
||||
|
||||
## 6. 翻页控制器对比
|
||||
## 6. 翻页控制器与宿主层对比
|
||||
|
||||
| 维度 | 微信读书 | ReadViewSDK |
|
||||
|------|---------|-------------|
|
||||
| 翻页动画 | 4 种:curl / slide / fade / none | 4 种:pageCurl / horizontalScroll / verticalScroll / horizontalCoverScroll |
|
||||
| UIPageViewController 崩溃修复 | 3 个 patch 方法处理已知 Apple bug(导航方向崩溃、子 VC 丢失、快速翻页动画故障) | 无 crash patch |
|
||||
| 导航方向检测 | `detectNavigationDirectionCrashWithPageViewController:` | 无 |
|
||||
| 维度 | 读书 | ReadViewSDK | 核查 |
|
||||
|------|---------|-------------|------|
|
||||
| 翻页动画 | curl / slide / fade / none | 已有多种翻页模式 | ✅ 已实现 |
|
||||
| `UIPageViewController` crash patch | 3 个以上专项 patch | 已有 pageCurl fault detect + 异步重建 | ⚠️ 有差异/有问题 |
|
||||
| 预加载 | `WRForecastUtils` 预测 + 后台准备 | 已围绕当前页与预测方向预热相邻页视图 | ⚠️ 有差异/有问题 |
|
||||
| 预测翻页 | 基于用户方向预测预建内容 | 已按可见 spread 和预测方向预热缓存 | ⚠️ 有差异/有问题 |
|
||||
| 显示切换一致性 | 翻页模式/单双页/方向切换稳定恢复 | 已接入切换前记录 location、切换后按位置回落 | ⚠️ 有差异/有问题 |
|
||||
|
||||
**核查:** `❌ 未实现`。这一块目前仍没有 WXRead 那类 page controller crash patch。
|
||||
**结论:** Phase 6 已进入“有实现、继续细抠”的阶段,不再是空白;但离 WXRead 的完整宿主层稳定性还有差距。
|
||||
|
||||
---
|
||||
|
||||
## 7. CSS 资源文件对比
|
||||
## 7. 资源体系对比
|
||||
|
||||
### 微信读书 CSS 文件(`resources/css/`)
|
||||
### CSS
|
||||
|
||||
| 文件 | 职责 | ReadViewSDK 对应 |
|
||||
|------|------|-----------------|
|
||||
| `default.css` | 基础 HTML 标签样式(font 18px, line-height 30px) | `RDEPUBStyleSheetBuilder` 注入的基础 CSS |
|
||||
| `replace.css` | EPUB 书版排版(6 级标题、首字下沉、引用、图片居中) | `RDEPUBTextRendererSupport.replaceCSS()`(部分覆盖) |
|
||||
| `dark.css` | 暗黑主题(纯黑背景 + 灰色文字) | `RDEPUBReaderTheme` 的 CSS 计算属性 |
|
||||
| `replaceForLatinLanguageBook.css` | 拉丁语言字体 | 无对应 |
|
||||
| `replaceForMPChapter.css` | 公众号文章专用 | 无对应(非 EPUB 场景) |
|
||||
| 文件 | 读书职责 | ReadViewSDK 状态 | 核查 |
|
||||
|------|-------------|-----------------|------|
|
||||
| `default.css` | 基础 HTML 标签样式 | 已有等价基础 CSS | ⚠️ 有差异/有问题 |
|
||||
| `replace.css` | 标题、图片、引用、分页控制 | 已有等价 replace 层 | ⚠️ 有差异/有问题 |
|
||||
| `dark.css` | 暗色主题 | 已有 dark 层 | ⚠️ 有差异/有问题 |
|
||||
| `replaceForLatinLanguageBook.css` | 拉丁语言字体 | 无 | ❌ 未实现 |
|
||||
| `replaceForMPChapter.css` | 公众号文章专用 | EPUB 主链路不需要 | ❌ 未实现(暂不需要) |
|
||||
|
||||
**核查:**
|
||||
- `default.css` / `dark.css` / `replace.css` 思路已落地,但规则集合仍不是 WXRead 原文件的完整镜像,属于 `⚠️ 有差异/有问题`。
|
||||
- `replaceForLatinLanguageBook.css` 仍是 `❌ 未实现`。
|
||||
- `replaceForMPChapter.css` 属于非 EPUB 场景,当前可视为 `❌ 未实现(暂不需要)`。
|
||||
### JS
|
||||
|
||||
### 微信读书 JS 文件(`resources/js/`)
|
||||
| 文件 | 读书职责 | ReadViewSDK 状态 | 核查 |
|
||||
|------|-------------|-----------------|------|
|
||||
| `weread-highlighter.js` | Web 高亮引擎 | `epub-bridge.js` 已接住高亮/批注展示 | ⚠️ 有差异/有问题 |
|
||||
| `rangy-*` | 选区/高亮库 | CoreText 路径不需要,Web 路径也未采用 | ⚠️ 有差异/有问题 |
|
||||
| `cssInjector.js` | 动态 CSS 注入 | 现为原生侧静态注入 | ⚠️ 有差异/有问题 |
|
||||
| `WeReadApi.js` | JS-Native 桥接 | `window.RDReaderBridge` | ⚠️ 有差异/有问题 |
|
||||
|
||||
| 文件 | 职责 | ReadViewSDK 对应 |
|
||||
|------|------|-----------------|
|
||||
| `weread-highlighter.js` | Rangy 高亮引擎,5 种样式 | `epub-bridge.js` + `RDEPUBWebView+JavaScriptBridge.swift` |
|
||||
| `rangy-core.js` / `rangy-highlighter.js` / `rangy-textrange.js` | Rangy 选区/高亮库 | 无(CoreText 路径不需要) |
|
||||
| `Readability.js` | HTML 正文提取 | 无(EPUB spine 直接提供内容) |
|
||||
| `cssInjector.js` | 动态 CSS 注入/移除 | 无(静态注入) |
|
||||
| `WeReadApi.js` | JS-Native 桥接 | `epub-bridge.js`(`window.RDReaderBridge`) |
|
||||
|
||||
**核查:** 这一节整体仍然成立。WebView 路径没有按 WXRead 的 JS 资源体系一一复刻,属于 `⚠️ 有差异/有问题`,但对纯 EPUB CoreText 路径不是当前主阻塞。
|
||||
**结论:** 不建议也不应该把 WXRead 私有 JS/CSS 资源整包原样搬用;当前应继续走“能力等价、实现自有”的路线。
|
||||
|
||||
---
|
||||
|
||||
## 8. 缺失能力对比
|
||||
## 8. 缺失能力清单
|
||||
|
||||
| 能力 | 微信读书 | ReadViewSDK | 核查 | 当前优先级 |
|
||||
|------|---------|-------------|------|-----------|
|
||||
| CoreText 直接绘制 | `WRPageView.drawRect:` + `CTFrameDraw` | 已是页面级 CoreText 直绘,但选区/装饰层还未完全 CoreText 化 | ⚠️ 有差异/有问题 | P1 |
|
||||
| 分页引擎集成 | `WRCoreTextLayouter` 完整集成 | 已集成到 builder | ✅ 已实现 | - |
|
||||
| CSS `<link>` 处理 | 级联合并外部 CSS | 已处理 | ✅ 已实现 | - |
|
||||
| 5 层 CSS 级联 | default/replace/dark/epub/user | 已生成并注入 | ✅ 已实现 | - |
|
||||
| 分页缓存 | `WRChapterPageCount.currentCacheKeyWithBookId:` | 已有磁盘缓存 | ✅ 已实现 | - |
|
||||
| 字符级位置精度 | `WREpubPositionConverter` | progression 浮点值 | ❌ 未实现 | P2 |
|
||||
| 字体动态加载 | CDN + `WRFontsManager` | 仅系统字体 | ❌ 未实现 | P1(延迟) |
|
||||
| 繁简转换 | `WRReaderCht2sManager` | 无 | ❌ 未实现 | P3 |
|
||||
| TTS 朗读 | wxtts 引擎 + 位置标记 | 无 | ❌ 未实现 | P3 |
|
||||
| DRM | 逐章加密 + Keychain 密钥 | 无 | ❌ 未实现 | P3 |
|
||||
| 预加载 | `WRForecastUtils` 预测 + 后台下载 | 无 | ❌ 未实现 | P3 |
|
||||
| Apple Pencil | PencilKit + COS 云同步 | 无 | ❌ 未实现 | P3 |
|
||||
| 多栏排版 | `WRCoreTextLayoutConfig.columns/columnGap` | 无 | ❌ 未实现 | P3 |
|
||||
| 能力 | 核查 | 当前优先级 |
|
||||
|------|------|-----------|
|
||||
| 页面几何模型完全等价 `WRCoreTextLayoutFrame` | ⚠️ 有差异/有问题 | P1 |
|
||||
| `RDEPUBChapterData` 最终内聚 | ⚠️ 有差异/有问题 | P2 |
|
||||
| 完整字符级位置转换器 | ⚠️ 有差异/有问题 | P2 |
|
||||
| 翻页 crash patch 完整版 | ⚠️ 有差异/有问题 | P3 |
|
||||
| 预加载 | ⚠️ 有差异/有问题 | P3 |
|
||||
| 预测翻页 | ⚠️ 有差异/有问题 | P3 |
|
||||
| 显示切换一致性 | ⚠️ 有差异/有问题 | P3 |
|
||||
| 多栏排版 | ❌ 未实现 | P4 |
|
||||
| 字体动态加载 | ❌ 未实现 | P4 |
|
||||
| 繁简转换 | ❌ 未实现 | P4 |
|
||||
| TTS / DRM / Pencil | ❌ 未实现 | P4 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 优先级排序与 Roadmap 映射
|
||||
## 9. 收口路线(整合原修复计划)
|
||||
|
||||
| 优先级 | 问题 | 核查 | 对应 Phase / 建议 | 说明 |
|
||||
|--------|------|------|-------------------|------|
|
||||
| ~~P0~~ | 集成 `RDEPUBTextLayouter` 到 `RDEPUBTextBookBuilder` | ✅ 已完成 | 已落地 | 不再是待办 |
|
||||
| ~~P0~~ | CSS `<link>` 外部样式表处理 | ✅ 已完成 | 已落地 | 不再是待办 |
|
||||
| ~~P0~~ | 5 层 CSS 级联 | ✅ 已完成 | 已落地 | 不再是待办 |
|
||||
| ~~P0~~ | 分页缓存 | ✅ 已完成 | 已落地 | 不再是待办 |
|
||||
| **P1** | CoreText 原生选区 / hit test | ❌ 未实现 | 新增 phase | 当前仍主要依赖 `UITextView` |
|
||||
| **P1** | 标注/搜索独立 CGContext 装饰层 | ❌ 未实现 | 新增 phase | 当前仍用 attributedString 属性注入 |
|
||||
| **P1** | 页面几何与选区 overlay 收口 | ⚠️ 半成品 | 延续 page-geometry phase | `RDEPUBSelectionOverlayView` 仍未闭环 |
|
||||
| **P1** | inline attachment 细节对齐 | ⚠️ 已接近但仍需验书 | 延续 pagination/rendering phase | 尤其脚注图标、特殊 inline image |
|
||||
| **P1** | 字体系统 | ❌ 未实现 | 未来版本 | 内嵌固定字体集,延迟实现 |
|
||||
| **P2** | 位置精度提升 | ❌ 未实现 | 独立重构 | 字符级锚点 / 双向转换 |
|
||||
| **P2** | 章节数据模型内聚 | ❌ 未实现 | 独立重构 | WRChapterData 模式 |
|
||||
| **P3** | TTS / DRM / Pencil / 多栏 | ❌ 未实现 | 独立功能模块 | 后续迭代 |
|
||||
### P1:页面几何与分页行为闭环
|
||||
|
||||
- `RDEPUBPageLayoutSnapshot` 继续补齐到更接近 `WRCoreTextLayoutFrame`
|
||||
- overlay / decoration / selection 回查能力继续统一
|
||||
- 继续结合问题书压平标题、脚注段、`weread-page-relate`、attachment、缩进段等分页细节
|
||||
|
||||
当前状态:`⚠️ 进行中`
|
||||
|
||||
### P2:位置模型与章节数据内聚
|
||||
|
||||
- 把 `rangeAnchor` / `fileIndex/row/column` 继续收成完整位置转换器
|
||||
- 把 controller / book 层散落的正文语义继续迁入 `RDEPUBChapterData`
|
||||
|
||||
当前状态:`⚠️ 进行中`
|
||||
|
||||
### P3:宿主层稳定性
|
||||
|
||||
- `pageCurl` crash patch 完整化
|
||||
- 预加载细化
|
||||
- 预测翻页细化
|
||||
- 显示切换一致性继续压平
|
||||
|
||||
当前状态:`⚠️ 进行中`
|
||||
|
||||
### P4:外围能力
|
||||
|
||||
- 多栏
|
||||
- 字体系统
|
||||
- 繁简转换
|
||||
- TTS / DRM / Pencil
|
||||
|
||||
当前状态:`❌ 未开始`
|
||||
|
||||
---
|
||||
|
||||
## 10. 微信读书关键类职责速查
|
||||
## 10. 读书关键类职责速查
|
||||
|
||||
| 类名 | 职责 | ReadViewSDK 对应 | 核查 |
|
||||
|------|------|-----------------|------|
|
||||
| `WRReaderViewController` | 阅读器主控制器(431 方法) | `RDEPUBReaderController` | ✅ 已有对应 |
|
||||
| `WRPageViewController` | 翻页控制器(含 3 个 crash patch) | `RDReaderView`(pageCurl 模式) | ⚠️ 有差异/无 crash patch |
|
||||
| `WRReaderViewController` | 阅读器主控制器 | `RDEPUBReaderController` | ✅ 已有对应 |
|
||||
| `WRPageViewController` | 翻页控制器 + crash patch | `RDReaderView` | ⚠️ 有差异/有问题 |
|
||||
| `WRPageView` | CoreText 直接绘制页面 | `RDEPUBTextContentView` + `RDEPUBDirectCoreTextPageView` | ✅ 已实现 |
|
||||
| `WREpubParser` | EPUB 解析 | `RDEPUBParser` | ✅ 已有对应 |
|
||||
| `WREpubTypesetter` | HTML → NSAttributedString(CSS 级联) | `RDEPUBDTCoreTextRenderer` | ✅ 已有对应 |
|
||||
| `WREpubTypesetter` | HTML → NSAttributedString(CSS 级联) | `RDEPUBDTCoreTextRenderer` | ✅ 已实现 |
|
||||
| `WRCoreTextLayouter` | CoreText 排版引擎 | `RDEPUBTextLayouter` | ✅ 已实现 |
|
||||
| `WRCoreTextLayoutFrame` | 排版帧(绘制/选区/搜索/装饰) | `RDEPUBTextLayoutFrame` | ⚠️ 绘制/分页接近,选区/装饰未闭环 |
|
||||
| `WRChapterData` | 章节数据模型(渲染+标注+目录一体) | `RDEPUBTextChapter` + `RDEPUBHighlight`(分散) | ❌ 未实现 |
|
||||
| `WRChapterPageCount` | 分页计算器(含缓存 key) | `RDEPUBTextBookBuilder` + `RDEPUBTextBookCache` | ✅ 已实现 |
|
||||
| `WREpubPositionConverter` | 字符级位置双向转换 | `RDEPUBTextBook.pageNumber(for:)`(精度较低) | ❌ 未实现 |
|
||||
| `WRBookmark` | 5 种标注类型统一模型 | `RDEPUBHighlight` + `RDEPUBBookmark` | ⚠️ 部分对应 |
|
||||
| `WRCoreTextLayoutFrame` | 排版帧(绘制/选区/搜索/装饰) | `RDEPUBTextLayoutFrame` + snapshot/interaction/overlay | ⚠️ 有差异/有问题 |
|
||||
| `WRChapterData` | 章节数据模型 | `RDEPUBChapterData` | ⚠️ 有差异/有问题 |
|
||||
| `WRChapterPageCount` | 分页计算 + 缓存 key | `RDEPUBTextBookBuilder` + `RDEPUBTextBookCache` | ✅ 已实现 |
|
||||
| `WREpubPositionConverter` | 字符级位置双向转换 | `RDEPUBTextAnchor` + `RDEPUBTextIndexTable` | ⚠️ 有差异/有问题 |
|
||||
| `WRBookmark` | 标注统一模型 | `RDEPUBHighlight` + `RDEPUBBookmark` | ⚠️ 有差异/有问题 |
|
||||
| `DTHTMLAttributedStringBuilder` | HTML DOM → NSAttributedString | 同(复用 DTCoreText) | ✅ 已实现 |
|
||||
|
||||
---
|
||||
|
||||
## 数据流对比
|
||||
## 11. 当前数据流
|
||||
|
||||
### 微信读书
|
||||
|
||||
```
|
||||
```text
|
||||
EPUB 文件
|
||||
→ WREpubParser.parse (container.xml → OPF → spine)
|
||||
→ WREpubTypesetter (CSS 级联: default < replace < dark < epub < user)
|
||||
→ DTHTMLAttributedStringBuilder (HTML → NSAttributedString)
|
||||
→ WRCoreTextLayouter (CTFramesetter → CTFrame)
|
||||
→ WRChapterPageCount (分页 NSRange)
|
||||
→ WRPageView.drawRect (CTFrameDraw → CGContext)
|
||||
→ 屏幕显示
|
||||
```
|
||||
|
||||
### ReadViewSDK 当前(已按当前代码修正)
|
||||
|
||||
```
|
||||
EPUB 文件
|
||||
→ RDEPUBParser.parse (container.xml → OPF → spine)
|
||||
→ readingProfile 分流:
|
||||
-> RDEPUBParser.parse (container.xml -> OPF -> spine)
|
||||
-> readingProfile 分流:
|
||||
textReflowable:
|
||||
→ RDEPUBDTCoreTextRenderer (DTHTMLAttributedStringBuilder + 5 层 CSS + <link> CSS 内联)
|
||||
→ RDEPUBTextBookBuilder (rd_paginatedFrames + RDEPUBTextBookCache)
|
||||
→ RDEPUBTextContentView (页面级 CoreText 直绘 + UITextView 选区代理)
|
||||
-> RDEPUBDTCoreTextRenderer
|
||||
-> 5 层 CSS 级联
|
||||
-> <link> CSS 内联
|
||||
-> 自定义分页语义注入
|
||||
-> RDEPUBTextBookBuilder
|
||||
-> RDEPUBTextLayouter
|
||||
-> RDEPUBTextBookCache
|
||||
-> RDEPUBTextContentView
|
||||
-> RDEPUBDirectCoreTextPageView
|
||||
-> RDEPUBPageInteractionController
|
||||
-> RDEPUBSelectionOverlayView
|
||||
webInteractive:
|
||||
→ RDEPUBPaginator (离屏 WKWebView 分页)
|
||||
→ RDEPUBWebContentView (WKWebView 渲染)
|
||||
-> RDEPUBPaginator
|
||||
-> RDEPUBWebContentView
|
||||
-> epub-bridge.js
|
||||
webFixedLayout:
|
||||
→ RDEPUBWebContentView (WKWebView + fixed-layout template)
|
||||
-> RDEPUBWebContentView
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*分析基础:微信读书 v10.0.3 (Build 79) 逆向工程文档*
|
||||
*产出:10 个维度对比、14 项差距识别、优先级排序*
|
||||
*分析基础:读书 v10.0.3 (Build 79) 逆向文档*
|
||||
*当前代码核查日期:2026-05-24*
|
||||
|
||||
Reference in New Issue
Block a user