ReadViewSDK/Doc/WXRead/decompiled-doc.md

14 KiB
Raw Blame History

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:jumpReadingToNextChapterjumpReadingToPreChapter
  • 页面渲染: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: 直接绘制文本,不使用 UILabelUITextView

核心绘制:

  • 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.xmlcontent.opfspine → 章节文件

主要 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 的转换核心。全部为类方法,无需实例化。

主入口方法:

+ (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 手绘

关键属性: bookmarkIdbookIdchapterUidchapterOffsetmarkTextstartPos/endPoscolorStyleanchorIdrangeKeysyncKey

持久化: toDictionary / fromDictionarytoJSONData / 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 / fromDictionarytoJSONData / 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 做了大量私有定制。

DTHTMLAttributedStringBuilder

HTML DOM → NSAttributedString 的 SAX 风格解析构建器。WREpubTypesetter 的底层依赖。

WeRead 自定义属性键:

  • DTPageBackgroundColorAttribute / DTPageBackgroundImageAttribute — 页面背景
  • DTPageBreakAfterAttribute / DTPageBreakBeforeAttribute — 分页控制
  • DTPageBreakInsideAvoidAttribute — 跨页避让
  • DTPageRelateAttribute — 跨页关联
  • DTHTMLVerticalCenterAttribute — 垂直居中
  • DTHTMLTranslateTagAttribute — 翻译标签

DTHTMLElement

HTML 元素 DOM 节点。构建解析时的 DOM 树,每个节点可通过 attributedString 方法生成对应的属性字符串。

关键属性: tagNameparent/childrenstyleDictionaryisBlockElementshouldAvoidPageBreakInside

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
翻页展示