16 KiB
架构对比分析:微信读书 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
标注说明:
✅ 已实现:当前代码已经按接近 WXRead 的路径落地⚠️ 有差异/有问题:已经实现一部分,但仍与 WXRead 有结构差异,或者当前实现仍有已知问题❌ 未实现:当前代码中仍缺失
核心结论
微信读书使用双渲染引擎架构(Path A: 原生 CoreText / Path B: WebView)。ReadViewSDK 当前也已经是 CoreText 文本页 + WebView 双路径,但文本页仍保留 UITextView 作为选区代理层,因此“页面绘制”已接近 WXRead,“选区/装饰/命中测试”仍未完全收口到同一套 CoreText 页面模型。
核查摘要
✅ 已按 WXRead 路径落地
- 文本页已经改成页面级 CoreText 直绘,不再是单纯
UITextView展示。 RDEPUBTextLayouter已经接入RDEPUBTextBookBuilder/RDPlainTextBookBuilder。- 4 级语义断页、
avoidPageBreakInside页尾回退、分页缓存都已经落地。 - 5 层 CSS 级联和 EPUB
<link>外部样式表内联都已经落地。
❌ 仍未实现
- CoreText 原生 hit test / 选区命中。
- 独立 CGContext 标注绘制层与搜索高亮层。
- WXRead 式字符级位置模型(
fileIndex + row + column)。 WRChapterData一体化章节数据模型。- UIPageViewController crash patch、多栏、字体动态加载、TTS/DRM 等外围能力。
⚠️ 已实现但仍有问题/差异
- 当前文本页虽然已直绘,但仍依赖
UITextView代理选区,页面几何模型没有完全统一。 RDEPUBSelectionOverlayView仍是半成品,尚未形成完整可用的 CoreText 选区/装饰 overlay。- 高亮、下划线、搜索命中仍主要靠 attributedString 属性注入,而不是 WXRead 那种页面装饰绘制。
- inline attachment 已按 WXRead 路径收敛,但仍需要继续结合问题书样验证特殊图片、脚注图标和页内基线细节。
1. 渲染路径对比
| 维度 | 微信读书 | 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 注入临时高亮属性 | ⚠️ 有差异/有问题 |
核查结论: 这段原分析已经过时。当前文本页的底层排版和绘制已经切到 CoreText 页面直绘,但“命中测试、标注装饰层、搜索高亮层”还没有完全像 WXRead 那样归并到同一套页面级 CoreText 几何模型。
2. 分页引擎对比
| 维度 | 微信读书 | 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 处理 |
✅ 已实现 |
核查结论: 这段原判断已经过时。RDEPUBTextBookBuilder 和 RDPlainTextBookBuilder 已经走 rd_paginatedFrames(size:fragmentOffsets:),不再是“能力已写但未接入”的状态。
3. CSS 处理对比
| 维度 | 微信读书 | 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 文件/规则 | ⚠️ 有差异/有问题 |
核查结论: “未处理 <link> 外部 CSS” 这一条已经不成立;但外部 CSS 即使已被内联,规则兼容度和 WXRead 自带 replace/default 体系仍可能存在差异。
4. 位置模型对比
| 维度 | 微信读书 | ReadViewSDK |
|---|---|---|
| 位置模型 | WREpubPositionConverter — (fileIndex, row, column) 三元组 |
RDEPUBLocation — href + progression 浮点值 |
| 精度 | 字符级精度 | 章节内比例精度 (0..1) |
| 索引构建 | initIndices 预建全书字符索引表 |
无索引,每次实时计算 |
| 双向转换 | stringRangeFromFileRange: / fileIndexForCharacterPosition: / localOffsetInFileAtIndex:forGlobalPosition: |
RDEPUBTextBook.pageNumber(for:) / location(forPageNumber:) |
影响分析: progression 是 0..1 浮点值,在跨字号/行距重排后能保持大致位置,但无法精确到字符。微信读书的三元组提供字符级精度,适合精确标注恢复。当前 progression 模式对大部分场景够用,但在密集标注场景下可能出现标注漂移。
核查: ❌ 未实现。当前位置模型仍然不是 WXRead 的字符级三元组。
5. 章节数据模型对比
| 维度 | 微信读书 | ReadViewSDK |
|---|---|---|
| 核心模型 | WRChapterData — 一个对象承载渲染结果 + 标注 + 目录 + 免费试读截断 |
RDEPUBTextChapter + RDEPUBHighlight + EPUBChapterInfo 分散在多处 |
| 标注内聚 | addHighlightInRange: / addUnderLineToAttributedString: 直接在 chapterData 上操作 |
标注在 RDEPUBReaderController 层管理,与章节数据分离 |
| 目录生成 | WRChapterData.generateOutlineContents — 从渲染结果反向提取目录 |
目录从 NCX/Nav 独立解析,不与渲染数据关联 |
核查: ❌ 未实现。章节数据模型仍未收敛成 WXRead 的 WRChapterData 一体化模式。
6. 翻页控制器对比
| 维度 | 微信读书 | ReadViewSDK |
|---|---|---|
| 翻页动画 | 4 种:curl / slide / fade / none | 4 种:pageCurl / horizontalScroll / verticalScroll / horizontalCoverScroll |
| UIPageViewController 崩溃修复 | 3 个 patch 方法处理已知 Apple bug(导航方向崩溃、子 VC 丢失、快速翻页动画故障) | 无 crash patch |
| 导航方向检测 | detectNavigationDirectionCrashWithPageViewController: |
无 |
核查: ❌ 未实现。这一块目前仍没有 WXRead 那类 page controller crash patch。
7. CSS 资源文件对比
微信读书 CSS 文件(resources/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 场景) |
核查:
default.css/dark.css/replace.css思路已落地,但规则集合仍不是 WXRead 原文件的完整镜像,属于⚠️ 有差异/有问题。replaceForLatinLanguageBook.css仍是❌ 未实现。replaceForMPChapter.css属于非 EPUB 场景,当前可视为❌ 未实现(暂不需要)。
微信读书 JS 文件(resources/js/)
| 文件 | 职责 | 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 路径不是当前主阻塞。
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 |
9. 优先级排序与 Roadmap 映射
| 优先级 | 问题 | 核查 | 对应 Phase / 建议 | 说明 |
|---|---|---|---|---|
集成 RDEPUBTextLayouter 到 RDEPUBTextBookBuilder |
✅ 已完成 | 已落地 | 不再是待办 | |
CSS <link> 外部样式表处理 |
✅ 已完成 | 已落地 | 不再是待办 | |
| 5 层 CSS 级联 | ✅ 已完成 | 已落地 | 不再是待办 | |
| 分页缓存 | ✅ 已完成 | 已落地 | 不再是待办 | |
| 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 / 多栏 | ❌ 未实现 | 独立功能模块 | 后续迭代 |
10. 微信读书关键类职责速查
| 类名 | 职责 | ReadViewSDK 对应 | 核查 |
|---|---|---|---|
WRReaderViewController |
阅读器主控制器(431 方法) | RDEPUBReaderController |
✅ 已有对应 |
WRPageViewController |
翻页控制器(含 3 个 crash patch) | RDReaderView(pageCurl 模式) |
⚠️ 有差异/无 crash patch |
WRPageView |
CoreText 直接绘制页面 | RDEPUBTextContentView + RDEPUBDirectCoreTextPageView |
✅ 已实现 |
WREpubParser |
EPUB 解析 | RDEPUBParser |
✅ 已有对应 |
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 |
⚠️ 部分对应 |
DTHTMLAttributedStringBuilder |
HTML DOM → NSAttributedString | 同(复用 DTCoreText) | ✅ 已实现 |
数据流对比
微信读书
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 分流:
textReflowable:
→ RDEPUBDTCoreTextRenderer (DTHTMLAttributedStringBuilder + 5 层 CSS + <link> CSS 内联)
→ RDEPUBTextBookBuilder (rd_paginatedFrames + RDEPUBTextBookCache)
→ RDEPUBTextContentView (页面级 CoreText 直绘 + UITextView 选区代理)
webInteractive:
→ RDEPUBPaginator (离屏 WKWebView 分页)
→ RDEPUBWebContentView (WKWebView 渲染)
webFixedLayout:
→ RDEPUBWebContentView (WKWebView + fixed-layout template)
分析基础:微信读书 v10.0.3 (Build 79) 逆向工程文档 产出:10 个维度对比、14 项差距识别、优先级排序