- Rename source module from RDReaderView to RDEpubReaderView - Move all source files from Sources/RDReaderView/ to Sources/RDEpubReaderView/ - Update podspec: RDReaderView.podspec -> RDEpubReaderView.podspec - Update Podfile, demo project, and CocoaPods config for new pod name - Delete old RDReaderView pod support files from ReadViewDemo/Pods - Add new RDEpubReaderView pod support files - Update documentation (API ref, architecture, UML, conventions, etc.) - Add FixedLayoutRotationTests - Update .gitignore: exclude .DS_Store, manual unpack backups, _ssoft-output
17 KiB
EPUBTextRendering 模块代码级参考文档
最后更新:2026-06-18
1. 模块概述
EPUBTextRendering 是 ReadViewSDK 的文本渲染层,负责将 EPUB HTML 内容转换为原生 NSAttributedString,执行排版分页,并构建全书文本模型。它位于 EPUBCore 之上、EPUBUI 之下,是 textReflowable 阅读配置文件的核心引擎。
文件清单(~30 个 Swift 文件):
| 子系统 | 核心文件 | 职责 |
|---|---|---|
| 排版管线 | Typesetter/RDEPUBTypesettingPipeline.swift |
HTML → 标记化 HTML 的多阶段管线 |
| 文本渲染 | RDEPUBTextRenderer.swift, RDEPUBDTCoreTextRenderer.swift |
HTML → NSAttributedString 转换 |
| 分页引擎 | Pagination/*.swift |
CoreText 排版、页帧计算、分页策略 |
| 构建管线 | BuildPipeline/*.swift |
全书构建(渲染 → 分页 → 缓存) |
| 搜索 | RDEPUBTextSearchEngine.swift |
基于原生文本的全文搜索 |
| 索引 | RDEPUBTextIndexTable.swift |
全书字符偏移索引、CFI 映射 |
| 位置转换 | RDEPUBTextPositionConverter.swift |
字符偏移 ↔ 页码转换 |
| 章节数据 | RDEPUBChapterData.swift |
章节级数据访问封装 |
2. 排版管线(Typesetter)
2.1 RDEPUBTextTypesettingPipeline
文件: Typesetter/RDEPUBTypesettingPipeline.swift
多阶段 HTML 处理管线,将原始 HTML 转换为可渲染的标记化 HTML。
struct RDEPUBTextTypesetterPipeline {
func makeRequest(from input: RDEPUBTypesettingInput) -> RDEPUBTypesettingOutput
}
管线阶段:
原始 HTML
│
▼
RDEPUBHTMLNormalizer → 清理 HTML(移除脚本、修复标签)
│
▼
RDEPUBSemanticMarkerInjector → 注入语义标记(段落、列表、表格等)
│
▼
RDEPUBCFIMarkerInjector → 注入 CFI 定位标记
│
▼
RDEPUBStyleSheetComposer → 合并样式表(内联 CSS + 兼容性处理)
│
▼
RDEPUBFontNormalizer → 注册嵌入字体、字体回退
│
▼
RDEPUBFragmentMarkerInjector → 注入片段标记(用于锚点定位)
│
▼
标记化 HTML + 诊断报告
2.2 管线输入/输出
struct RDEPUBTypesettingInput {
var href: String // 章节 href
var spineIndex: Int?
var title: String // 章节标题
var rawHTML: String // 原始 HTML
var baseURL: URL? // 资源基础 URL
var style: RDEPUBTextRenderStyle // 渲染样式
var resourceResolver: RDEPUBResourceResolver?
var contentLanguageCode: String?
var pageSize: CGSize?
var layoutConfig: RDEPUBTextLayoutConfig?
}
struct RDEPUBTypesettingOutput {
var request: RDEPUBTextChapterRenderRequest // 渲染请求
var diagnostics: [RDEPUBTextResourceReferenceDiagnostic] // 资源引用诊断
var styleCompatibilityReport: RDEPUBCSSCompatibilityReport // CSS 兼容性报告
}
2.3 各阶段处理器
| 处理器 | 文件 | 职责 |
|---|---|---|
RDEPUBHTMLNormalizer |
RDEPUBHTMLNormalizer.swift |
清理 HTML:移除 <script>、修复未闭合标签、规范化编码 |
RDEPUBSemanticMarkerInjector |
RDEPUBSemanticMarkerInjector.swift |
为块级元素注入 data-rd-block-kind 属性 |
RDEPUBCFIMarkerInjector |
RDEPUBCFIMarkerInjector.swift |
为文本节点注入 data-rd-cfi 属性 |
RDEPUBStyleSheetComposer |
RDEPUBStyleSheetComposer.swift |
合并 EPUB 原始 CSS + SDK 样式 + 兼容性补丁 |
RDEPUBFontNormalizer |
RDEPUBFontNormalizer.swift |
检测并注册 @font-face 嵌入字体 |
RDEPUBFragmentMarkerInjector |
RDEPUBFragmentMarkerInjector.swift |
为 id 属性注入片段偏移标记 |
RDEPUBAttachmentNormalizer |
RDEPUBAttachmentNormalizer.swift |
规范化 <img>、<svg> 等附件元素 |
RDEPUBRenderDiagnosticsCollector |
RDEPUBRenderDiagnosticsCollector.swift |
收集渲染诊断信息(缺失资源、不支持的特性) |
2.4 CSS 兼容性层
文件: Typesetter/Compatibility/
| 文件 | 职责 |
|---|---|
RDEPUBCSSCompatibilityLayer.swift |
CSS 兼容性补丁(webkit 前缀、不支持属性替换) |
RDEPUBFontFallbackResolver.swift |
字体回退链解析 |
RDEPUBStyleCompatibilityModels.swift |
兼容性报告模型 |
3. 文本渲染器
3.1 RDEPUBTextRenderer(协议)
文件: RDEPUBTextRenderer.swift
public protocol RDEPUBTextRenderer {
/// 将 HTML 渲染为 NSAttributedString
func render(html: String, baseURL: URL?, style: RDEPUBTextRenderStyle) -> NSAttributedString
}
3.2 RDEPUBDTCoreTextRenderer
文件: RDEPUBDTCoreTextRenderer.swift
基于 DTCoreText 的渲染器实现,将 HTML 转换为 NSAttributedString。
public final class RDEPUBDTCoreTextRenderer: RDEPUBTextRenderer {
public init()
public func render(html: String, baseURL: URL?, style: RDEPUBTextRenderStyle) -> NSAttributedString
}
3.3 渲染样式
public struct RDEPUBTextRenderStyle {
public var font: UIFont // 字体
public var lineSpacing: CGFloat // 行间距
public var textColor: UIColor? // 文字颜色
public var backgroundColor: UIColor? // 背景颜色
}
3.4 NSAttributedString 扩展键
extension NSAttributedString.Key {
static let rdPageBlockRange // 块级元素范围
static let rdPageBlockIndex // 块索引
static let rdPageFragmentID // 片段 ID
static let rdPageAttachmentKind // 附件类型
static let rdPageBlockKind // 块类型(paragraph/list/table/code/blockquote/attachment/generic)
static let rdPageSemanticHints // 语义提示(avoidPageBreakInside/keepWithNext/pageBreakBefore/After)
static let rdPageAttachmentPlacement // 附件放置方式(inline/baseline/centered)
}
4. 分页引擎
4.1 RDEPUBTextLayouter
文件: Pagination/RDEPUBTextLayouter.swift
分页的核心入口,组合 RDEPUBCoreTextPageFrameFactory 和 RDEPUBChapterPageCounter。
struct RDEPUBTextLayouter {
init(attributedString: NSAttributedString, pageSize: CGSize, config: RDEPUBTextLayoutConfig = .default)
func layoutFrames(fragmentOffsets: [String: Int] = [:]) -> [RDEPUBTextLayoutFrame]
}
4.2 RDEPUBTextLayoutConfig
public struct RDEPUBTextLayoutConfig: Equatable {
public var frameWidth: CGFloat // 帧宽度
public var frameHeight: CGFloat // 帧高度
public var edgeInsets: UIEdgeInsets // 内边距
public var numberOfColumns: Int // 列数
public var columnGap: CGFloat // 列间距
public var avoidOrphans: Bool // 避免孤行
public var avoidWidows: Bool // 避免寡行
public var avoidPageBreakInsideEnabled: Bool // 避免块内分页
public var hyphenation: Bool // 连字符断词
public static let `default`: RDEPUBTextLayoutConfig
}
4.3 RDEPUBCoreTextPageFrameFactory
文件: Pagination/RDEPUBCoreTextPageFrameFactory.swift
使用 CoreText 排版引擎,将 NSAttributedString 排列到指定尺寸的帧中。
struct RDEPUBCoreTextPageFrameFactory {
init(attributedString: NSAttributedString, pageSize: CGSize, config: RDEPUBTextLayoutConfig)
func makeFrames(fragmentOffsets: [String: Int] = [:]) -> [RDEPUBTextLayoutFrame]
}
4.4 RDEPUBChapterPageCounter
文件: Pagination/RDEPUBChapterPageCounter.swift
基于 RDEPUBCoreTextPageFrameFactory 计算页范围。
struct RDEPUBChapterPageCounter {
init(factory: RDEPUBCoreTextPageFrameFactory)
func layoutFrames(fragmentOffsets: [String: Int] = [:]) -> [RDEPUBTextLayoutFrame]
}
4.5 RDEPUBTextLayoutFrame
文件: Pagination/RDEPUBTextLayoutFrame.swift
表示一个排版帧(一页的内容范围)。
public struct RDEPUBTextLayoutFrame {
public let range: NSRange // 字符范围
public let frameIndex: Int // 帧索引
public let diagnostics: [String] // 诊断信息
}
4.6 RDEPUBPageBreakPolicy
文件: Pagination/RDEPUBPageBreakPolicy.swift
分页策略,决定在何处断页。
struct RDEPUBPageBreakPolicy {
/// 检查指定位置是否允许分页
func canBreak(at offset: Int, in attributedString: NSAttributedString) -> Bool
}
5. 构建管线
5.1 RDEPUBTextBookBuilder
文件: BuildPipeline/RDEPUBTextBookBuilder.swift
全书构建的核心类,串联渲染、分页、缓存和诊断。
public final class RDEPUBTextBookBuilder {
public init(renderer: RDEPUBTextRenderer, cache: RDEPUBTextBookCache?, layoutConfig: RDEPUBTextLayoutConfig)
/// 构建全书文本模型
public func build(
parser: RDEPUBParser,
publication: RDEPUBPublication,
pageSize: CGSize,
style: RDEPUBTextRenderStyle
) throws -> RDEPUBTextBook
// 诊断信息
public private(set) var lastBuildResourceDiagnostics: [RDEPUBTextResourceReferenceDiagnostic]
public private(set) var lastBuildPaginationDiagnostics: [RDEPUBTextChapterPaginationDiagnostic]
public private(set) var lastBuildPerformanceSamples: [RDEPUBTextPerformanceSample]
public private(set) var lastBuildCacheStats: (hits: Int, misses: Int)
}
构建流程:
RDEPUBParser + RDEPUBPublication
│
▼ 遍历 spine(linear 章节)
│
├── 检查缓存(RDEPUBPaginationCacheCoordinator)
│ ├── 命中 → 直接使用缓存的分页结果
│ └── 未命中 → 执行渲染 + 分页
│
├── RDEPUBChapterRenderPipeline(渲染单章)
│ └── RDEPUBTextTypesetterPipeline → RDEPUBTextRenderer
│
├── RDEPUBChapterPaginationPipeline(分页单章)
│ └── RDEPUBTextLayouter → [RDEPUBTextLayoutFrame]
│
├── RDEPUBChapterTailNormalizer(章节尾部规范化)
│
└── 组装 RDEPUBTextChapter + RDEPUBTextPage
│
▼
RDEPUBTextBook(全书模型)
5.2 辅助组件
| 文件 | 职责 |
|---|---|
RDEPUBChapterRenderPipeline.swift |
单章渲染管线 |
RDEPUBChapterPaginationPipeline.swift |
单章分页管线 |
RDEPUBChapterTailNormalizer.swift |
章节尾部空白处理 |
RDEPUBPaginationCacheCoordinator.swift |
分页缓存协调 |
RDEPUBTextBookCache.swift |
分页缓存存储 |
RDEPUBTextPerformanceSampler.swift |
性能采样 |
RDEPUBBuildDiagnosticsReporter.swift |
构建诊断报告 |
RDEPUBTextBuildPipelineInterfaces.swift |
管线接口定义 |
6. 全书模型
6.1 RDEPUBTextBook
文件: BuildPipeline/RDEPUBTextBookModels.swift
全书文本模型,包含所有章节和页面。
public struct RDEPUBTextBook {
public var chapters: [RDEPUBTextChapter] // 所有章节
public var pages: [RDEPUBTextPage] // 所有页面(扁平化)
public let indexTable: RDEPUBTextIndexTable // 索引表
public var positionConverter: RDEPUBTextPositionConverter
// 章节查询
public func chapterData(for href: String) -> RDEPUBChapterData?
public func chapterData(forSpineIndex spineIndex: Int) -> RDEPUBChapterData?
public func chapterData(atChapterIndex index: Int) -> RDEPUBChapterData?
public func chapterData(forPageNumber pageNumber: Int) -> RDEPUBChapterData?
// 页码查询
public func page(at pageNumber: Int) -> RDEPUBTextPage?
public func pageNumber(for location: RDEPUBLocation, resolver: RDEPUBResourceResolver, bookIdentifier: String?) -> Int?
public func location(forPageNumber pageNumber: Int, bookIdentifier: String?) -> RDEPUBLocation?
}
6.2 RDEPUBTextChapter
public struct RDEPUBTextChapter: Equatable {
public var chapterIndex: Int // 章节索引
public var spineIndex: Int // spine 索引
public var href: String // 章节 href
public var title: String // 章节标题
public var attributedContent: NSAttributedString // 章节完整富文本
public var fragmentOffsets: [String: Int] // 片段 ID → 字符偏移
public var cfiMap: RDEPUBCFIMap? // CFI 映射
public var pageBreakReasons: [RDEPUBTextPageBreakReason] // 分页原因
public var pages: [RDEPUBTextPage] // 章节内页面
}
6.3 RDEPUBTextPage
public struct RDEPUBTextPage: Equatable {
public var absolutePageIndex: Int // 全书绝对页码
public var chapterIndex: Int // 章节索引
public var spineIndex: Int // spine 索引
public var href: String // 章节 href
public var chapterTitle: String // 章节标题
public var pageIndexInChapter: Int // 章节内页码
public var totalPagesInChapter: Int // 章节总页数
public var chapterContent: NSAttributedString // 章节完整内容
public var content: NSAttributedString // 页面内容(子范围)
public var contentRange: NSRange // 在章节内容中的范围
public var pageStartOffset: Int // 起始偏移
public var pageEndOffset: Int // 结束偏移
public var metadata: RDEPUBTextPageMetadata // 页元数据
}
7. 索引与位置转换
7.1 RDEPUBTextIndexTable
文件: RDEPUBTextIndexTable.swift
全书字符偏移索引,支持快速查找。
public struct RDEPUBTextIndexTable {
public let chapterStartOffsets: [Int] // 各章节起始偏移
public let chapterLengths: [Int] // 各章节长度
public let hrefToChapterIndex: [String: Int] // href → 章节索引
public let hrefToFileIndex: [String: Int] // href → 文件索引
public let fragmentOffsetsByHref: [String: [String: Int]] // 片段偏移
public let fileRowColumnMap: [Int: [RDEPUBRowColumnIndex]] // 行列映射
public let fileCFIMap: [Int: RDEPUBCFIMap] // CFI 映射
public var totalCharacterCount: Int // 总字符数
public init(chapters: [RDEPUBTextChapter])
}
7.2 RDEPUBTextPositionConverter
文件: RDEPUBTextPositionConverter.swift
字符偏移 ↔ 页码双向转换。
public struct RDEPUBTextPositionConverter {
init(book: RDEPUBTextBook)
/// 字符偏移 → 页码
public func pageNumber(for offset: Int) -> Int?
/// 页码 → 字符偏移范围
public func offsetRange(forPageNumber pageNumber: Int) -> NSRange?
}
7.3 RDEPUBChapterData
文件: RDEPUBChapterData.swift
章节级数据访问封装。
public struct RDEPUBChapterData {
public let chapter: RDEPUBTextChapter
public let indexTable: RDEPUBTextIndexTable
/// 位置 → 页码
public func pageNumber(for location: RDEPUBLocation) -> Int?
/// 页面 → 位置
public func location(for page: RDEPUBTextPage, bookIdentifier: String?) -> RDEPUBLocation?
/// 字符范围 → 范围锚点
public func rangeAnchor(for range: NSRange) -> RDEPUBRangeAnchor
}
8. 搜索引擎
文件: RDEPUBTextSearchEngine.swift
基于原生 NSAttributedString 的搜索引擎,比 HTML 搜索更高效。
final class RDEPUBTextSearchEngine: RDEPUBSearchEngine {
init(textBook: RDEPUBTextBook, publication: RDEPUBPublication)
func search(keyword: String) -> [RDEPUBSearchMatch]
}
搜索结果包含:href、progression、previewText、rangeAnchor、cfi 等定位信息。
9. 纯文本构建器
文件: RDEpubPlainTextBookBuilder.swift
从纯文本(.txt)文件构建 RDEPUBTextBook,用于支持 TXT 格式阅读。
10. 设计模式总结
| 模式 | 应用 |
|---|---|
| 管线模式 | RDEPUBTextTypesettingPipeline 多阶段 HTML 处理 |
| 策略模式 | RDEPUBTextRenderer 协议,RDEPUBDTCoreTextRenderer 实现 |
| Builder 模式 | RDEPUBTextBookBuilder 全书构建 |
| 缓存协调 | RDEPUBPaginationCacheCoordinator 分页结果缓存 |
| 索引表 | RDEPUBTextIndexTable 全书字符偏移快速查找 |
| 关注点分离 | 渲染/分页/缓存/诊断各司其职 |
11. 数据流图
EPUB HTML 文件
│
▼
RDEPUBTypesettingPipeline
│ HTMLNormalizer → SemanticMarker → CFI → StyleSheet → Font → Fragment
▼
标记化 HTML + CSS
│
▼
RDEPUBDTCoreTextRenderer
│ DTCoreText HTML → NSAttributedString
▼
NSAttributedString(带语义属性)
│
▼
RDEPUBTextLayouter
│ CoreText 排版 → 帧计算 → 分页策略
▼
[RDEPUBTextLayoutFrame](页面范围列表)
│
▼
RDEPUBTextChapter + RDEPUBTextPage
│
▼
RDEPUBTextBook(全书模型 + 索引表)