# Phase 1 Audit: Current Reflowable Engine, Boundaries, and Stable Contracts **Phase:** 01-current-engine-boundaries **Date:** 2026-05-21 **Purpose:** 固化当前 `.textReflowable` 旧引擎的真实调用链、模式分流边界、以及本次重构不能碰的外层契约。 ## 1. Executive Conclusion 当前仓库并不是“所有 EPUB 都走 `WKWebView`”。真实结构是三分流: 1. `webFixedLayout`:Fixed Layout EPUB,继续走 `WKWebView` 2. `webInteractive`:带脚本/多媒体/iframe/表单等交互内容的 EPUB,继续走 `WKWebView` 3. `textReflowable`:普通 reflowable EPUB,走 **DTCoreText -> `NSAttributedString` -> CoreText 分页 -> 原生文本页视图** 因此,本次“旧引擎直接演进”的主战场已经存在,且就在 `EPUBTextRendering`。Phase 2/3 不需要新起一套阅读器,也不需要改 `RDReaderView`;要做的是升级当前 native reflowable 的 typesetter、属性体系和分页能力。 ## 2. Entry Path: URL -> Reader Controller 入口在 `Sources/RDReaderView/RDURLReaderController.swift`。 ### 2.1 `.epub` 入口 `RDURLReaderController.embedReaderController()` 对扩展名做分支: - 扩展名是 `epub` - 直接构造 `RDEPUBReaderController(epubURL:configuration:)` - 把 reader controller 作为子控制器嵌入当前界面 这里没有直接决定 native 还是 web;它只负责把 `.epub` 交给 `RDEPUBReaderController`。 ### 2.2 `.txt` 入口 TXT 则走 `RDPlainTextBookBuilder` 构造 `RDEPUBTextBook`,再以 external TextBook 模式交给 `RDEPUBReaderController`。这条链路与 EPUB 的原生文本展示在 UI 层会合,但不参与 EPUB reading profile 判定。 ## 3. Reading Profile Split 模式分流在 `Sources/RDReaderView/EPUBCore/RDEPUBParser+ReadingProfile.swift`。 ### 3.1 判定规则 `RDEPUBParser.readingProfile()` 的真实逻辑是: - `metadata.layout == .fixed` -> `.webFixedLayout` - 否则,如果 `hasInteractiveContent() == true` -> `.webInteractive` - 否则 -> `.textReflowable` ### 3.2 `hasInteractiveContent()` 的判定来源 `hasInteractiveContent()` 会检查两类信号: - manifest 中是否出现 JS 相关 media type 或 `scripted` property - spine HTML 中是否出现交互模式特征 具体 regex 关注: - `