From 5698aeaeadbdc43671d4876a06e738574a7735f0 Mon Sep 17 00:00:00 2001
From: shen <>
Date: Fri, 22 May 2026 13:28:53 +0800
Subject: [PATCH] chore: checkpoint current milestone work
---
.planning/ROADMAP.md | 57 +-
.planning/STATE.md | 44 +-
.../phases/02-typesetter-css/02-01-PLAN.md | 92 +
.../phases/02-typesetter-css/02-01-SUMMARY.md | 68 +
.../phases/02-typesetter-css/02-02-PLAN.md | 93 +
.../phases/02-typesetter-css/02-02-SUMMARY.md | 70 +
.../phases/02-typesetter-css/02-03-PLAN.md | 93 +
.../phases/02-typesetter-css/02-03-SUMMARY.md | 67 +
.../phases/02-typesetter-css/02-PATTERNS.md | 32 +
.../phases/02-typesetter-css/02-RESEARCH.md | 177 +
.../phases/02-typesetter-css/02-VALIDATION.md | 76 +
.../02-typesetter-css/02-VERIFICATION.md | 70 +
.../03-page-metadata-pagination/03-01-PLAN.md | 105 +
.../03-01-SUMMARY.md | 20 +
.../03-page-metadata-pagination/03-02-PLAN.md | 106 +
.../03-02-SUMMARY.md | 21 +
.../03-page-metadata-pagination/03-03-PLAN.md | 106 +
.../03-03-SUMMARY.md | 19 +
.../03-PATTERNS.md | 31 +
.../03-RESEARCH.md | 144 +
.../03-VALIDATION.md | 63 +
.../03-VERIFICATION.md | 31 +
.../04-reader-capabilities/04-01-PLAN.md | 89 +
.../04-reader-capabilities/04-01-SUMMARY.md | 16 +
.../04-reader-capabilities/04-02-PLAN.md | 90 +
.../04-reader-capabilities/04-02-SUMMARY.md | 15 +
.../04-reader-capabilities/04-03-PLAN.md | 90 +
.../04-reader-capabilities/04-03-SUMMARY.md | 19 +
.../04-reader-capabilities/04-PATTERNS.md | 29 +
.../04-reader-capabilities/04-RESEARCH.md | 123 +
.../04-reader-capabilities/04-VALIDATION.md | 29 +
.../04-reader-capabilities/04-VERIFICATION.md | 32 +
.../05-regression-stability/05-01-PLAN.md | 84 +
.../05-regression-stability/05-01-SUMMARY.md | 21 +
.../05-regression-stability/05-02-PLAN.md | 101 +
.../05-regression-stability/05-02-SUMMARY.md | 16 +
.../05-regression-stability/05-PATTERNS.md | 27 +
.../05-regression-stability/05-RESEARCH.md | 122 +
.../05-regression-stability/05-VALIDATION.md | 46 +
.../05-VERIFICATION.md | 47 +
CONTEXT.md | 19 +
.../ReflowableEPUB_WXReadRenderer_Design.md | 16 +-
Doc/FeatureSolution/书签能力方案讨论.md | 398 ---
Doc/FeatureSolution/样书基线验证方案讨论.md | 439 ---
Doc/FeatureSolution/横竖屏切换支持方案讨论.md | 372 ---
Doc/FeatureSolution/自动化测试方案讨论.md | 418 ---
Podfile | 23 +-
ReadViewDemo/Podfile | 24 +-
ReadViewDemo/Podfile.lock | 2 +-
ReadViewDemo/Pods/Manifest.lock | 2 +-
.../Pods/Pods.xcodeproj/project.pbxproj | 2918 +++++++++--------
.../xcschemes/xcschememanagement.plist | 22 -
.../ReadViewDemo.xcodeproj/project.pbxproj | 31 +-
.../UserInterfaceState.xcuserstate | Bin 9415 -> 25055 bytes
.../ReadViewDemo/ViewController.swift | 444 ++-
.../ReadViewDemo/book/回归验证样本.txt | 11 +
.../EPUBCore/RDEPUBReadingModels.swift | 37 +
.../EPUBCore/RDEPUBResourceResolver.swift | 31 +-
.../RDEPUBDTCoreTextRenderer.swift | 62 +-
.../RDEPUBTextBookBuilder.swift | 101 +-
.../RDEPUBTextLayoutFrame.swift | 22 +
.../RDEPUBTextLayouter.swift | 244 ++
.../RDEPUBTextPaginationSupport.swift | 28 +-
.../RDEPUBTextRenderer.swift | 139 +-
.../RDEPUBTextRendererSupport.swift | 399 ++-
.../RDPlainTextBookBuilder.swift | 32 +-
.../EPUBUI/RDEPUBReaderController.swift | 154 +-
.../EPUBUI/RDEPUBTextContentView.swift | 16 +-
68 files changed, 5694 insertions(+), 3291 deletions(-)
create mode 100644 .planning/phases/02-typesetter-css/02-01-PLAN.md
create mode 100644 .planning/phases/02-typesetter-css/02-01-SUMMARY.md
create mode 100644 .planning/phases/02-typesetter-css/02-02-PLAN.md
create mode 100644 .planning/phases/02-typesetter-css/02-02-SUMMARY.md
create mode 100644 .planning/phases/02-typesetter-css/02-03-PLAN.md
create mode 100644 .planning/phases/02-typesetter-css/02-03-SUMMARY.md
create mode 100644 .planning/phases/02-typesetter-css/02-PATTERNS.md
create mode 100644 .planning/phases/02-typesetter-css/02-RESEARCH.md
create mode 100644 .planning/phases/02-typesetter-css/02-VALIDATION.md
create mode 100644 .planning/phases/02-typesetter-css/02-VERIFICATION.md
create mode 100644 .planning/phases/03-page-metadata-pagination/03-01-PLAN.md
create mode 100644 .planning/phases/03-page-metadata-pagination/03-01-SUMMARY.md
create mode 100644 .planning/phases/03-page-metadata-pagination/03-02-PLAN.md
create mode 100644 .planning/phases/03-page-metadata-pagination/03-02-SUMMARY.md
create mode 100644 .planning/phases/03-page-metadata-pagination/03-03-PLAN.md
create mode 100644 .planning/phases/03-page-metadata-pagination/03-03-SUMMARY.md
create mode 100644 .planning/phases/03-page-metadata-pagination/03-PATTERNS.md
create mode 100644 .planning/phases/03-page-metadata-pagination/03-RESEARCH.md
create mode 100644 .planning/phases/03-page-metadata-pagination/03-VALIDATION.md
create mode 100644 .planning/phases/03-page-metadata-pagination/03-VERIFICATION.md
create mode 100644 .planning/phases/04-reader-capabilities/04-01-PLAN.md
create mode 100644 .planning/phases/04-reader-capabilities/04-01-SUMMARY.md
create mode 100644 .planning/phases/04-reader-capabilities/04-02-PLAN.md
create mode 100644 .planning/phases/04-reader-capabilities/04-02-SUMMARY.md
create mode 100644 .planning/phases/04-reader-capabilities/04-03-PLAN.md
create mode 100644 .planning/phases/04-reader-capabilities/04-03-SUMMARY.md
create mode 100644 .planning/phases/04-reader-capabilities/04-PATTERNS.md
create mode 100644 .planning/phases/04-reader-capabilities/04-RESEARCH.md
create mode 100644 .planning/phases/04-reader-capabilities/04-VALIDATION.md
create mode 100644 .planning/phases/04-reader-capabilities/04-VERIFICATION.md
create mode 100644 .planning/phases/05-regression-stability/05-01-PLAN.md
create mode 100644 .planning/phases/05-regression-stability/05-01-SUMMARY.md
create mode 100644 .planning/phases/05-regression-stability/05-02-PLAN.md
create mode 100644 .planning/phases/05-regression-stability/05-02-SUMMARY.md
create mode 100644 .planning/phases/05-regression-stability/05-PATTERNS.md
create mode 100644 .planning/phases/05-regression-stability/05-RESEARCH.md
create mode 100644 .planning/phases/05-regression-stability/05-VALIDATION.md
create mode 100644 .planning/phases/05-regression-stability/05-VERIFICATION.md
create mode 100644 CONTEXT.md
delete mode 100644 Doc/FeatureSolution/书签能力方案讨论.md
delete mode 100644 Doc/FeatureSolution/样书基线验证方案讨论.md
delete mode 100644 Doc/FeatureSolution/横竖屏切换支持方案讨论.md
delete mode 100644 Doc/FeatureSolution/自动化测试方案讨论.md
create mode 100644 ReadViewDemo/ReadViewDemo/book/回归验证样本.txt
create mode 100644 Sources/RDReaderView/EPUBTextRendering/RDEPUBTextLayoutFrame.swift
create mode 100644 Sources/RDReaderView/EPUBTextRendering/RDEPUBTextLayouter.swift
diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index fe2e535..d24cc77 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -7,10 +7,10 @@
## Phases
- [x] **Phase 1: 对齐现状、边界与重构切入点** - 明确当前旧引擎的真实调用链、分流边界与必须保留的兼容能力 (completed 2026-05-21)
-- [ ] **Phase 2: 重构 typesetter 与 CSS 分层** - 在旧引擎基础上引入 WXRead 风格样式组织与章节级 HTML → attributed string 转换增强
-- [ ] **Phase 3: 重构属性体系与复杂分页器** - 引入页面级 attributed string 元数据与更强的分页/布局能力
-- [ ] **Phase 4: 接回现有 reader 能力链路** - 让新内核与阅读位置、高亮、搜索、主题切换等现有功能继续协作
-- [ ] **Phase 5: 回归验证与稳定性收敛** - 以样本书和主流程为核心完成回归、修复与验收
+- [x] **Phase 2: 重构 typesetter 与 CSS 分层** - 在旧引擎基础上引入 WXRead 风格样式组织与章节级 HTML → attributed string 转换增强 (completed 2026-05-22)
+- [x] **Phase 3: 重构属性体系与复杂分页器** - 引入页面级 attributed string 元数据与更强的分页/布局能力 (completed 2026-05-22)
+- [x] **Phase 4: 接回现有 reader 能力链路** - 让新内核与阅读位置、高亮、搜索、主题切换等现有功能继续协作 (completed 2026-05-22)
+- [x] **Phase 5: 回归验证与稳定性收敛** - 以样本书和主流程为核心完成回归、修复与验收 (completed 2026-05-22)
## Phase Details
@@ -40,9 +40,9 @@ Plans:
**Plans**: 3 plans
Plans:
-- [ ] 02-01:设计并实现旧引擎中的 WXRead 风格 stylesheet builder / HTML 预处理增强
-- [ ] 02-02:改造 `RDEPUBDTCoreTextRenderer` 与相邻渲染链路,使其承接新的样式分层与章节上下文
-- [ ] 02-03:验证章节级图片/CSS/基础资源在新渲染输入下可正常解析
+- [x] 02-01:设计并实现旧引擎中的 WXRead 风格 stylesheet builder / HTML 预处理增强
+- [x] 02-02:改造 `RDEPUBDTCoreTextRenderer` 与相邻渲染链路,使其承接新的样式分层与章节上下文
+- [x] 02-03:验证章节级图片/CSS/基础资源在新渲染输入下可正常解析
### Phase 3: 重构属性体系与复杂分页器
**Goal**: 在旧引擎路径中引入页面级 attributed string 元数据与更复杂的分页/页面布局能力,替代当前简单 `pageRanges` 切页模式。
@@ -55,9 +55,18 @@ Plans:
**Plans**: 3 plans
Plans:
-- [ ] 03-01:定义并实现页面级 attributed string 元数据与自定义属性键
-- [ ] 03-02:在旧引擎基础上重构分页器,使其具备接近 `WRCoreTextLayouter / WRCoreTextLayoutFrame` 的核心能力
-- [ ] 03-03:验证复杂块元素、图片与分页边界控制在新分页器下可工作
+**Wave 1**
+- [x] 03-01:定义并实现页面级 attributed string 元数据与自定义属性键
+
+**Wave 2** *(blocked on Wave 1 completion)*
+- [x] 03-02:在旧引擎基础上重构分页器,使其具备接近 `WRCoreTextLayouter / WRCoreTextLayoutFrame` 的核心能力
+
+**Wave 3** *(blocked on Wave 2 completion)*
+- [x] 03-03:验证复杂块元素、图片与分页边界控制在新分页器下可工作
+
+Cross-cutting constraints:
+- `pageStartOffset`、`pageEndOffset`、chapter text continuity、`fragmentOffsets` 必须保持为稳定兼容字段
+- 不修改 `RDReaderView`,不碰 fixed / interactive `WKWebView` 路径
### Phase 4: 接回现有 reader 能力链路
**Goal**: 让新内核在不新增并行原生引擎、且不修改当前翻页代码的前提下,继续服务现有 reader UI、阅读位置、高亮、搜索与主题切换能力。
@@ -71,9 +80,14 @@ Plans:
**Plans**: 3 plans
Plans:
-- [ ] 04-01:将新内核接回 `RDEPUBTextBookBuilder` / `RDEPUBTextContentView` / `RDEPUBReaderController`,不修改 `RDReaderView`
-- [ ] 04-02:修复并验证阅读位置映射、高亮、搜索等兼容能力
-- [ ] 04-03:验证字体、行高、主题切换后的重新分页与状态恢复
+- [x] 04-01:将新内核接回 `RDEPUBTextBookBuilder` / `RDEPUBTextContentView` / `RDEPUBReaderController`,不修改 `RDReaderView`
+- [x] 04-02:修复并验证阅读位置映射、高亮、搜索等兼容能力
+- [x] 04-03:验证字体、行高、主题切换后的重新分页与状态恢复
+
+Cross-cutting constraints:
+- `RDReaderView` 继续只作为现有翻页容器使用,不承接新的 native page metadata 责任
+- native text 兼容能力必须继续围绕 `RDEPUBLocation`、`pageStartOffset` / `pageEndOffset`、`RDEPUBTextOffsetRangeInfo` 工作
+- 主题 / 字号 / 行高 / viewport 变化后的恢复应以 semantic location 为主,而不是 page number
### Phase 5: 回归验证与稳定性收敛
**Goal**: 围绕样本书和主流程做回归,收敛分页正确性、位置映射稳定性与关键阅读交互问题。
@@ -86,15 +100,20 @@ Plans:
**Plans**: 2 plans
Plans:
-- [ ] 05-01:构建样本书验证矩阵与诊断手段(日志/断言/复现清单)
-- [ ] 05-02:收敛分页、位置映射、图片/块元素分页与主题切换后的稳定性问题
+- [x] 05-01:构建样本书验证矩阵与诊断手段(日志/断言/复现清单)
+- [x] 05-02:收敛分页、位置映射、图片/块元素分页与主题切换后的稳定性问题
+
+Cross-cutting constraints:
+- Phase 5 以验证和收敛修复为主,不重新打开更大范围的架构改造
+- 样本矩阵必须覆盖 native reflowable、fixed/interactive WebKit 路径,以及 TXT 兼容路径
+- 每项回归结论都应尽量附着到可重复证据:runtime summary、日志、断言或清单
## Progress
| Phase | Plans Complete | Status | Completed |
|---|---:|---|---|
| 1. 对齐现状、边界与重构切入点 | 2/2 | Complete | 2026-05-21 |
-| 2. 重构 typesetter 与 CSS 分层 | 0/3 | Not started | - |
-| 3. 重构属性体系与复杂分页器 | 0/3 | Not started | - |
-| 4. 接回现有 reader 能力链路 | 0/3 | Not started | - |
-| 5. 回归验证与稳定性收敛 | 0/2 | Not started | - |
+| 2. 重构 typesetter 与 CSS 分层 | 3/3 | Complete | 2026-05-22 |
+| 3. 重构属性体系与复杂分页器 | 3/3 | Complete | 2026-05-22 |
+| 4. 接回现有 reader 能力链路 | 3/3 | Complete | 2026-05-22 |
+| 5. 回归验证与稳定性收敛 | 2/2 | Complete | 2026-05-22 |
diff --git a/.planning/STATE.md b/.planning/STATE.md
index 756916b..b6eb997 100644
--- a/.planning/STATE.md
+++ b/.planning/STATE.md
@@ -2,20 +2,20 @@
gsd_state_version: 1.0
milestone: v1.0
milestone_name: milestone
-status: ready_to_plan
-last_updated: 2026-05-21T13:59:55.225Z
+status: ready_to_complete_milestone
+last_updated: 2026-05-22T05:22:00.000Z
progress:
total_phases: 5
- completed_phases: 0
- total_plans: 2
- completed_plans: 2
- percent: 0
-stopped_at: Phase 01 complete (2/2) — ready to discuss Phase 2
+ completed_phases: 5
+ total_plans: 13
+ completed_plans: 13
+ percent: 100
+stopped_at: Phase 05 executed and verified — ready for milestone completion
---
# STATE
-**状态日期:** 2026-05-21
+**状态日期:** 2026-05-22
**项目:** ReadViewSDK(brownfield)
## Project Reference
@@ -23,23 +23,20 @@ stopped_at: Phase 01 complete (2/2) — ready to discuss Phase 2
参见:`.planning/PROJECT.md`(更新于 2026-05-21)
**核心价值:** 稳定可用的 EPUB/TXT 阅读体验
-**当前关注:** Phase 2(重构 typesetter 与 CSS 分层)待 planning / execute
+**当前关注:** Phase 5(回归验证与稳定性收敛)已执行完成,下一步可进入 milestone 收尾
## 当前结论(摘要)
-- 项目范围已从“增强现有 reflowable renderer”升级为“**直接重构现有 reflowable EPUB 旧引擎**”。
-- `textReflowable` 将在旧引擎基础上演进为参考 WXRead 的原生渲染方案,不保留并行的第二套原生引擎。
-- `webFixedLayout` 与 `webInteractive` 继续使用 `WKWebView`,不在本次范围内替换。
-- 当前翻页代码(`RDReaderView` 及现有翻页模式/交互逻辑)明确不修改,新内核必须适配现有翻页容器。
-- 本次重构必须继续兼容:阅读位置映射、高亮/选区、搜索结果定位、字号/行高/主题切换后的重新分页。
+- 全部 5 个 phase 已完成,native reflowable、fixed/interactive WebKit、TXT 三条主路径均有 Phase 5 运行时证据。
+- Demo 启动摘要现在输出 5/5 样本矩阵结果,并保留 Phase 3-4 的 pagination / restore diagnostics。
+- TXT 样本已内置到 `ReadViewDemo/ReadViewDemo/book/回归验证样本.txt`,不再依赖执行期临时文件。
+- `RDReaderView` 仍保持不修改;本轮里程碑以验证收敛结束,没有重新打开更大架构改造。
## 当前风险焦点
-- 分页正确性
-- 位置映射稳定性
-- 图片/块元素分页
-- 高亮与搜索链路回归
-- 主题和字号切换后的重排一致性
+- 仍缺少“每本样本都全自动点读一遍”的 UI 自动化;当前证据以启动矩阵、日志和一次 reader UI 快照为主
+- simulator 仍会输出既有 `.SFUI-Semibold` CoreText 替代提示,但未造成功能性失败
+- 若后续进入发布前验收,建议补一轮更细的交互式手动清单或 UI 自动化
## 现有代码库地图
@@ -54,5 +51,10 @@ stopped_at: Phase 01 complete (2/2) — ready to discuss Phase 2
## 下一步
-- 运行:`$gsd-plan-phase 2`(为旧引擎的 WXRead 风格 typesetter / CSS 分层重构生成可执行计划)
-- 已完成的 Phase 1 产物目录:`.planning/phases/01-current-engine-boundaries/`
+- 建议下一步:`$gsd-complete-milestone`
+- 已完成的产物目录:
+ - `.planning/phases/01-current-engine-boundaries/`
+ - `.planning/phases/02-typesetter-css/`
+ - `.planning/phases/03-page-metadata-pagination/`
+ - `.planning/phases/04-reader-capabilities/`
+ - `.planning/phases/05-regression-stability/`
diff --git a/.planning/phases/02-typesetter-css/02-01-PLAN.md b/.planning/phases/02-typesetter-css/02-01-PLAN.md
new file mode 100644
index 0000000..b1be77c
--- /dev/null
+++ b/.planning/phases/02-typesetter-css/02-01-PLAN.md
@@ -0,0 +1,92 @@
+---
+phase: 02-typesetter-css
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextStyleSheetBuilder.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
+autonomous: true
+requirements:
+ - REND-02
+user_setup: []
+must_haves:
+ truths:
+ - native renderer has an explicit chapter preprocessing / stylesheet assembly layer rather than only a flat DTCoreText option bag.
+ - CSS precedence is deterministic and documented as `default / replace / dark / epub / user`.
+ - chapter baseURL and resource context are preserved through preprocessing.
+ artifacts:
+ - .planning/phases/02-typesetter-css/02-01-SUMMARY.md
+ key_links:
+ - `RDEPUBTextBookBuilder` remains the chapter boundary and provides render context.
+ - `RDEPUBStyleSheetBuilder` is not repurposed for the native path.
+---
+
+
+设计并实现旧引擎中的 WXRead 风格 stylesheet builder / HTML 预处理增强。
+
+Purpose: 把章节 HTML、baseURL、资源引用和用户/主题/EPUB 样式合成为一个明确的 native renderer 输入层。
+Output: `Sources/RDReaderView/EPUBTextRendering/` 中新增或重构的 stylesheet / preprocessing helper,以及与其对接的 renderer entry points。
+
+
+
+@$HOME/.codex/get-shit-done/workflows/execute-plan.md
+@$HOME/.codex/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/02-typesetter-css/02-RESEARCH.md
+@.planning/phases/02-typesetter-css/02-PATTERNS.md
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
+@Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift
+@Sources/RDReaderView/EPUBUI/RDEPUBReaderTheme.swift
+@Sources/RDReaderView/EPUBCore/RDEPUBPreferences.swift
+@Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift
+
+
+
+
+
+ Task 1: 建立 native stylesheet / preprocessing helper
+ Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
+ Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift, Sources/RDReaderView/EPUBCore/RDEPUBPreferences.swift, Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift, Sources/RDReaderView/EPUBUI/RDEPUBReaderTheme.swift
+ 在 `EPUBTextRendering` 内新增或重构一个明确的 native stylesheet/preprocessing helper:它需要把 `RDEPUBPreferences` / `RDEPUBReaderTheme` / `RDEPUBTextRenderStyle` 里的输入翻译成可叠加的 CSS 层,并在 chapter import 前完成 HTML 规范化、style tag 组装和必要的资源上下文占位。实现时要把层级顺序写死为 `default / replace / dark / epub / user`,并把它暴露成可复用的 renderer input,而不是继续在 `dtOptions` 里堆零散参数。
+ rg -n "default|replace|dark|epub|user|stylesheet|preprocess|chapter|baseURL" Sources/RDReaderView/EPUBTextRendering
+
+ - native renderer 侧出现独立的 stylesheet / preprocessing 责任
+ - CSS 层级顺序是显式且可读的
+ - chapter baseURL 与资源上下文没有被抹掉
+
+ 章节样式输入不再是一个扁平 option bag,而是一个可追踪、可验证的 native 样式层管线。
+
+
+
+
+
+Before declaring plan complete:
+- [ ] `test -f Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift`
+- [ ] `rg -n "default|replace|dark|epub|user|preprocess|stylesheet" Sources/RDReaderView/EPUBTextRendering`
+- [ ] 计划文档明确拒绝复用 WebView 专用的 `RDEPUBStyleSheetBuilder`
+
+
+
+
+- All tasks completed
+- All verification checks pass
+- No errors or warnings introduced
+- native renderer 的样式层输入足以支撑 Phase 2 后半段的 renderer 接线
+
+
+
diff --git a/.planning/phases/02-typesetter-css/02-01-SUMMARY.md b/.planning/phases/02-typesetter-css/02-01-SUMMARY.md
new file mode 100644
index 0000000..eac6ef6
--- /dev/null
+++ b/.planning/phases/02-typesetter-css/02-01-SUMMARY.md
@@ -0,0 +1,68 @@
+---
+phase: 02-typesetter-css
+plan: 01
+subsystem: epub-text-rendering
+tags: [epub, dtcoretext, css, stylesheet, preprocessing]
+requires: []
+provides:
+ - "native renderer 的章节级 stylesheet / preprocessing 输入层"
+ - "显式的 CSS 五层顺序 default / replace / dark / epub / user"
+ - "章节 baseURL 与资源诊断信息"
+affects: [phase-02, phase-03, reflowable-engine]
+tech-stack:
+ added: []
+ patterns: ["chapter render request", "stylesheet layering", "resource-aware preprocessing"]
+key-files:
+ created: []
+ modified:
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift
+key-decisions:
+ - "不复用 WebView 专用的 `RDEPUBStyleSheetBuilder`,而是在 native path 中建立独立的 chapter preprocessing pipeline。"
+ - "把 EPUB 外链 CSS 预处理为原位内联样式,并在内联时重写 `url(...)` 资源路径。"
+patterns-established:
+ - "章节输入先做 HTML 规范化、baseURL 注入、stylesheet 分层,再交给 renderer。"
+requirements-completed: []
+duration: 35min
+completed: 2026-05-22
+---
+
+# Phase 02 Plan 01: Typesetter Input Layer Summary
+
+**为 native reflowable renderer 建立了独立的 chapter stylesheet / preprocessing 层,输入不再只是扁平 DTCoreText options。**
+
+## Performance
+
+- **Duration:** 35 min
+- **Completed:** 2026-05-22
+- **Tasks:** 1
+- **Files modified:** 2
+
+## Accomplishments
+
+- 在 [RDEPUBTextRenderer.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift) 中引入了 `RDEPUBTextChapterContext`、`RDEPUBTextChapterRenderRequest`、`RDEPUBTextStyleSheetPackage` 与资源诊断模型。
+- 在 [RDEPUBTextRendererSupport.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift) 中实现了章节 HTML 规范化、`` 注入、linked CSS 内联、`url(...)` 重写,以及 `default / replace / dark / epub / user` 五层 CSS 组装。
+- 样式层顺序被硬编码进 preprocessing 管线,native renderer 现在消费的是明确的 chapter render request,而不是一组分散选项。
+
+## Files Created/Modified
+
+- [RDEPUBTextRenderer.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift) - 定义 chapter context、stylesheet package 与 resource diagnostics
+- [RDEPUBTextRendererSupport.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift) - 实现 preprocessing、CSS 分层、资源解析与 HTML 注入
+
+## Decisions Made
+
+- 明确拒绝把 WebView 分页路径的 `RDEPUBStyleSheetBuilder` 拉回 native renderer。
+- EPUB 外链 CSS 保持为 EPUB 层,但通过预处理内联进入统一管线,以确保 user layer 始终处于最后覆盖位。
+
+## Verification
+
+- `rg -n "default|replace|dark|epub|user|stylesheet|preprocess|chapter|baseURL" Sources/RDReaderView/EPUBTextRendering`
+- `xcodebuild` simulator build passed via `ReadViewDemo` scheme
+
+## Next Phase Readiness
+
+- Renderer 已经有显式 chapter context 和 stylesheet output,Plan 02 可以直接把 `RDEPUBDTCoreTextRenderer` 接到这个新契约上。
+
+---
+*Phase: 02-typesetter-css*
+*Completed: 2026-05-22*
diff --git a/.planning/phases/02-typesetter-css/02-02-PLAN.md b/.planning/phases/02-typesetter-css/02-02-PLAN.md
new file mode 100644
index 0000000..ce92928
--- /dev/null
+++ b/.planning/phases/02-typesetter-css/02-02-PLAN.md
@@ -0,0 +1,93 @@
+---
+phase: 02-typesetter-css
+plan: 02
+type: execute
+wave: 2
+depends_on:
+ - "02-01"
+files_modified:
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
+ - Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift
+ - Sources/RDReaderView/EPUBCore/RDEPUBParser.swift
+autonomous: true
+requirements:
+ - REND-01
+ - REND-02
+user_setup: []
+must_haves:
+ truths:
+ - `RDEPUBDTCoreTextRenderer` receives explicit chapter context and stylesheet output.
+ - `RDEPUBTextBookBuilder` continues to own chapter boundary and page model assembly.
+ - reflowable EPUB input is no longer just a small builder option bag.
+ artifacts:
+ - .planning/phases/02-typesetter-css/02-02-SUMMARY.md
+ key_links:
+ - `RDEPUBParser.readingProfile()` still keeps `webFixedLayout` and `webInteractive` out of this path.
+ - `RDEPUBResourceResolver` remains the authority for baseURL/resource normalization.
+---
+
+
+改造 `RDEPUBDTCoreTextRenderer` 与相邻渲染链路,使其承接新的样式分层与章节上下文。
+
+Purpose: 让 native renderer 真正消费 Phase 2.1 产出的 style pipeline,并保持章节边界、资源上下文和 fallback 行为稳定。
+Output: 对 renderer 和 book builder 的接线调整,使 chapter render path 具备明确的输入契约。
+
+
+
+@$HOME/.codex/get-shit-done/workflows/execute-plan.md
+@$HOME/.codex/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/02-typesetter-css/02-RESEARCH.md
+@.planning/phases/02-typesetter-css/02-PATTERNS.md
+@.planning/phases/02-typesetter-css/02-01-PLAN.md
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift
+@Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift
+@Sources/RDReaderView/EPUBCore/RDEPUBParser.swift
+
+
+
+
+
+ Task 1: 接入 chapter context 并简化 renderer 输入
+ Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift
+ 把 chapter 级上下文显式传入 renderer:包括 `href`、`baseURL`、样式层输出、以及后续资源解析所需的最小元数据。`RDEPUBTextBookBuilder` 继续负责拼接章节、构造 `RDEPUBTextChapter` / `RDEPUBTextPage`,但不再只传一个“扁平 style”给 renderer。`RDEPUBDTCoreTextRenderer` 应该从“DTCoreText option bag 的薄包装”转成“章节 typesetter 的统一入口”。
+ rg -n "chapter context|baseURL|stylesheet|RDEPUBDTCoreTextRenderer|RDEPUBTextBookBuilder|DTHTMLAttributedStringBuilder" Sources/RDReaderView/EPUBTextRendering
+
+ - renderer 入参能表达 chapter 级上下文
+ - renderer 仍保留 fallback path
+ - book builder 仍然是 page model 的唯一构造点
+
+ native renderer 的输入契约足够清楚,后续分页语义重构可以直接接在它后面。
+
+
+
+
+
+Before declaring plan complete:
+- [ ] `test -f Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift`
+- [ ] `rg -n "chapter context|baseURL|stylesheet|RDEPUBDTCoreTextRenderer|RDEPUBTextBookBuilder" Sources/RDReaderView/EPUBTextRendering`
+- [ ] 文档与 Phase 2.1 计划不冲突,并且没有把 WebView 路径拉回 native renderer
+
+
+
+
+- All tasks completed
+- All verification checks pass
+- No errors or warnings introduced
+- renderer 与 book builder 的职责边界足够稳定,Phase 2.3 可以直接用真实样本验证资源解析
+
+
+
diff --git a/.planning/phases/02-typesetter-css/02-02-SUMMARY.md b/.planning/phases/02-typesetter-css/02-02-SUMMARY.md
new file mode 100644
index 0000000..8273373
--- /dev/null
+++ b/.planning/phases/02-typesetter-css/02-02-SUMMARY.md
@@ -0,0 +1,70 @@
+---
+phase: 02-typesetter-css
+plan: 02
+subsystem: epub-text-rendering
+tags: [epub, dtcoretext, renderer, chapter-context]
+requires: [02-01]
+provides:
+ - "renderer 接收显式 chapter context"
+ - "book builder 到 renderer 的稳定输入契约"
+ - "resource diagnostics 沿渲染链路透传"
+affects: [phase-02, phase-03, reflowable-engine]
+tech-stack:
+ added: []
+ patterns: ["chapter-scoped renderer contract", "book-builder-owned pagination boundary"]
+key-files:
+ created: []
+ modified:
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ - Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift
+key-decisions:
+ - "`RDEPUBTextBookBuilder` 继续是 chapter boundary 与 page model assembly 的唯一持有者。"
+ - "`RDEPUBResourceResolver` 扩展为 chapter-relative 资源标准化的权威入口。"
+patterns-established:
+ - "renderer 从 request.context 读取 href/baseURL/stylesheet/resource diagnostics。"
+requirements-completed: []
+duration: 20min
+completed: 2026-05-22
+---
+
+# Phase 02 Plan 02: Renderer Wiring Summary
+
+**把新的 chapter stylesheet / preprocessing pipeline 正式接入 `RDEPUBDTCoreTextRenderer` 与 `RDEPUBTextBookBuilder`。**
+
+## Performance
+
+- **Duration:** 20 min
+- **Completed:** 2026-05-22
+- **Tasks:** 1
+- **Files modified:** 3
+
+## Accomplishments
+
+- [RDEPUBDTCoreTextRenderer.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift) 现在首先消费 `RDEPUBTextChapterRenderRequest`,再根据 request 中的 `baseURL` 与 style 生成 DTCoreText 输入。
+- [RDEPUBTextBookBuilder.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift) 在 chapter boundary 处组装 render request,并保留分页、chapter/page model 组装职责不变。
+- [RDEPUBResourceResolver.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift) 新增 chapter-relative `normalizedHref` / `fileURL(forReference:relativeToHref:)`,让资源标准化继续集中在 resolver 层。
+
+## Files Created/Modified
+
+- [RDEPUBDTCoreTextRenderer.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift) - renderer 改为 chapter-scoped 输入契约
+- [RDEPUBTextBookBuilder.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift) - builder 负责组装 request,并保留 page model 边界
+- [RDEPUBResourceResolver.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift) - chapter-relative 资源标准化与文件定位
+
+## Decisions Made
+
+- 保留 `renderChapter(html:baseURL:style:)` 兼容入口,但统一转发到新的 request-based renderer 契约。
+- 资源诊断跟随 render result 一起回到 builder,便于后续 demo/sample validation 使用真实 chapter 数据做 spot-check。
+
+## Verification
+
+- `rg -n "chapter context|baseURL|stylesheet|RDEPUBDTCoreTextRenderer|RDEPUBTextBookBuilder" Sources/RDReaderView/EPUBTextRendering`
+- `xcodebuild` simulator build passed via `ReadViewDemo` scheme
+
+## Next Phase Readiness
+
+- 真实样本验证现在可以直接读取 builder 的 `lastBuildResourceDiagnostics`,无需再创建并行 reader shell。
+
+---
+*Phase: 02-typesetter-css*
+*Completed: 2026-05-22*
diff --git a/.planning/phases/02-typesetter-css/02-03-PLAN.md b/.planning/phases/02-typesetter-css/02-03-PLAN.md
new file mode 100644
index 0000000..c09b636
--- /dev/null
+++ b/.planning/phases/02-typesetter-css/02-03-PLAN.md
@@ -0,0 +1,93 @@
+---
+phase: 02-typesetter-css
+plan: 03
+type: execute
+wave: 3
+depends_on:
+ - "02-02"
+files_modified:
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift
+ - Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift
+ - Sources/RDReaderView/EPUBCore/RDEPUBParser.swift
+ - ReadViewDemo/ReadViewDemo/ViewController.swift
+autonomous: true
+requirements:
+ - REND-01
+ - REND-02
+user_setup: []
+must_haves:
+ truths:
+ - sample EPUBs with images and linked CSS can still resolve chapter resources through the new native pipeline.
+ - baseURL and resource normalization are verified against real EPUB files, not just code review.
+ - the validation path stays on the current demo app and does not require a new reader shell.
+ artifacts:
+ - .planning/phases/02-typesetter-css/02-03-SUMMARY.md
+ key_links:
+ - `RDEPUBResourceResolver` and `RDEPUBParser` are the source of truth for normalized chapter/resource paths.
+ - sample books in `ReadViewDemo/ReadViewDemo/book/` are the regression corpus.
+---
+
+
+验证章节级图片/CSS/基础资源在新渲染输入下可正常解析。
+
+Purpose: 证明 Phase 2.1 和 Phase 2.2 的接线在真实 EPUB 上没有破坏资源加载和章节样式解析。
+Output: sample EPUB spot-checks、验证记录和必要的诊断补充。
+
+
+
+@$HOME/.codex/get-shit-done/workflows/execute-plan.md
+@$HOME/.codex/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/02-typesetter-css/02-RESEARCH.md
+@.planning/phases/02-typesetter-css/02-PATTERNS.md
+@.planning/phases/02-typesetter-css/02-01-PLAN.md
+@.planning/phases/02-typesetter-css/02-02-PLAN.md
+@Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift
+@Sources/RDReaderView/EPUBCore/RDEPUBParser.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+@ReadViewDemo/ReadViewDemo/ViewController.swift
+@ReadViewDemo/ReadViewDemo/book/
+
+
+
+
+
+ Task 1: 用真实样本书验证 chapter resource resolution
+ Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift
+ Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift, Sources/RDReaderView/EPUBCore/RDEPUBParser.swift, ReadViewDemo/ReadViewDemo/ViewController.swift
+ 使用 `ReadViewDemo/ReadViewDemo/book/` 下的 EPUB 作为验证集,确认新 pipeline 下章节 baseURL、相对 CSS、图片和其他资源仍能正确解析。必要时增加最小的诊断输出或 debug hook,但不要引入新的 reader shell。验证重点是“资源真的能被定位和加载”,不是只看渲染结果是否大致正常。
+ rg -n "baseURL|resource|css|image|stylesheet|normalized" Sources/RDReaderView/EPUBCore Sources/RDReaderView/EPUBTextRendering ReadViewDemo/ReadViewDemo
+
+ - sample EPUB 资源加载没有断裂
+ - relative href / baseURL 解析保持稳定
+ - demo app 仍然是验证入口
+
+ Phase 2 的 stylesheet / preprocessing 改造可以被真实书籍证实没有把资源寻址弄坏。
+
+
+
+
+
+Before declaring plan complete:
+- [ ] `test -d ReadViewDemo/ReadViewDemo/book`
+- [ ] `rg -n "baseURL|resource|css|image|stylesheet|normalized" Sources/RDReaderView/EPUBCore Sources/RDReaderView/EPUBTextRendering`
+- [ ] demo app 仍然只依赖现有 reader 主流程,没有新增并行引擎
+
+
+
+
+- All tasks completed
+- All verification checks pass
+- No errors or warnings introduced
+- 至少一套 sample EPUB 能证明章节资源和样式层在 native renderer 中是可工作的
+
+
+
diff --git a/.planning/phases/02-typesetter-css/02-03-SUMMARY.md b/.planning/phases/02-typesetter-css/02-03-SUMMARY.md
new file mode 100644
index 0000000..4e9e279
--- /dev/null
+++ b/.planning/phases/02-typesetter-css/02-03-SUMMARY.md
@@ -0,0 +1,67 @@
+---
+phase: 02-typesetter-css
+plan: 03
+subsystem: demo-validation
+tags: [epub, resources, validation, demo]
+requires: [02-02]
+provides:
+ - "真实 EPUB 样本资源验证"
+ - "demo 首页中的验证状态展示"
+ - "运行时日志里的验证结果"
+affects: [phase-02, phase-05, readviewdemo]
+tech-stack:
+ added: []
+ patterns: ["sample-corpus validation", "demo-surfaced diagnostics"]
+key-files:
+ created: []
+ modified:
+ - ReadViewDemo/ReadViewDemo/ViewController.swift
+key-decisions:
+ - "继续使用现有 demo app 作为验证入口,不新增 reader shell。"
+ - "以 `RDEPUBTextBookBuilder.lastBuildResourceDiagnostics` 为资源完整性判定依据。"
+patterns-established:
+ - "demo 启动时后台扫描 EPUB corpus,并把 pass/fail 汇总写到状态标签与运行时日志。"
+requirements-completed: []
+duration: 20min
+completed: 2026-05-22
+---
+
+# Phase 02 Plan 03: Sample EPUB Validation Summary
+
+**用 demo 中的真实 EPUB 样本验证了新 native pipeline 的章节资源定位,并把结果直接暴露给 demo UI 与 runtime log。**
+
+## Performance
+
+- **Duration:** 20 min
+- **Completed:** 2026-05-22
+- **Tasks:** 1
+- **Files modified:** 1
+
+## Accomplishments
+
+- 在 [ViewController.swift](/Users/shen/Work/Code/ReadViewSDK/ReadViewDemo/ReadViewDemo/ViewController.swift) 中增加后台 EPUB validation pass,自动解析 demo corpus、构建 `RDEPUBTextBook`,并检查 `lastBuildResourceDiagnostics` 是否存在缺失资源。
+- 状态标签现在会显示 `EPUB 资源验证:X/Y 通过`,运行时日志也会输出同样的汇总,便于 simulator 回归。
+- simulator 运行日志记录了:`[ReadViewDemo] EPUB 资源验证:2/2 通过`,证明两本 `.textReflowable` 样本书在新 pipeline 下资源解析通过。
+
+## Files Created/Modified
+
+- [ViewController.swift](/Users/shen/Work/Code/ReadViewSDK/ReadViewDemo/ReadViewDemo/ViewController.swift) - 样本校验、UI 文案展示、runtime log 输出
+
+## Decisions Made
+
+- 只把 `.textReflowable` 样本纳入这轮自动验证;`webFixedLayout` / `webInteractive` 仍由原有 WebView 路径负责。
+- 验证关注点限定为“资源是否被正确定位并进入渲染链路”,不在本阶段引入新的视觉比对基础设施。
+
+## Verification
+
+- `test -d ReadViewDemo/ReadViewDemo/book`
+- `rg -n "baseURL|resource|css|image|stylesheet|normalized" Sources/RDReaderView/EPUBCore Sources/RDReaderView/EPUBTextRendering ReadViewDemo/ReadViewDemo`
+- simulator runtime log: `[ReadViewDemo] EPUB 资源验证:2/2 通过`
+
+## Next Phase Readiness
+
+- Phase 3 可以在这个资源稳定输入层之上直接推进 attributed string 元数据和复杂分页器,不需要再回头补资源寻址契约。
+
+---
+*Phase: 02-typesetter-css*
+*Completed: 2026-05-22*
diff --git a/.planning/phases/02-typesetter-css/02-PATTERNS.md b/.planning/phases/02-typesetter-css/02-PATTERNS.md
new file mode 100644
index 0000000..b54e403
--- /dev/null
+++ b/.planning/phases/02-typesetter-css/02-PATTERNS.md
@@ -0,0 +1,32 @@
+# Phase 2: 重构 typesetter 与 CSS 分层 - Pattern Map
+
+## Goal
+
+为本阶段执行提供“先看哪里、按什么证据写代码”的最短路径。Phase 2 的核心不是分页,而是把章节 HTML、baseURL、资源解析和 CSS 层级变成明确的 native renderer 输入。
+
+## Planned Outputs
+
+| Planned file | Role | Primary evidence | Why this is the right analog |
+|--------------|------|------------------|------------------------------|
+| `.planning/phases/02-typesetter-css/02-01-PLAN.md` | 章节样式层与 HTML 预处理实现计划 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift`, `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift`, `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift` | 这些文件已经是 native renderer 的真实入口,最适合承接 stylesheet pipeline。 |
+| `.planning/phases/02-typesetter-css/02-02-PLAN.md` | renderer 与 chapter context 接线计划 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift`, `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift`, `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift` | 这三个文件共同构成 HTML 导入、章节边界和富文本输出链路。 |
+| `.planning/phases/02-typesetter-css/02-03-PLAN.md` | 资源解析与样本书验证计划 | `Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift`, `Sources/RDReaderView/EPUBCore/RDEPUBParser.swift`, `ReadViewDemo/ReadViewDemo/book/*.epub` | 资源解析必须靠真实 EPUB 资源和 chapter baseURL 证实,不宜只看代码。 |
+
+## Code Evidence Map
+
+| Concern | Closest source of truth | Evidence to extract |
+|---------|-------------------------|---------------------|
+| 章节输入如何进入 native renderer | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift` | chapter `href`、`baseURL`、`renderChapter(...)` 入参、页模型构造点。 |
+| 当前 renderer 输入到底有多薄 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBDTCoreTextRenderer.swift` | `DTHTMLAttributedStringBuilder`、`dtOptions(baseURL:style:)`、fallback path。 |
+| 现有 HTML 预处理做了什么 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift` | fragment marker 注入、reading attribute normalization、fallback attributed string。 |
+| 用户样式与主题从哪里来 | `Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift`, `Sources/RDReaderView/EPUBUI/RDEPUBReaderTheme.swift`, `Sources/RDReaderView/EPUBCore/RDEPUBPreferences.swift` | font size / line spacing / theme colors 如何进入 renderer 输入。 |
+| 哪些 CSS helper 不能直接复用 | `Sources/RDReaderView/EPUBCore/RDEPUBStyleSheetBuilder.swift` | 这是 WebView 量测/分页 helper,不是 native DTCoreText stylesheet pipeline。 |
+| 资源如何定位 | `Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift` | 相对路径、manifest item、OPF directory、normalized href。 |
+| 哪些样本书最有代表性 | `ReadViewDemo/ReadViewDemo/book/*.epub` | 含图片、复杂段落、外链 CSS 的样本最适合验证 chapter-scoped styles。 |
+
+## Writing Guidance
+
+- 计划文档先把新的 native stylesheet pipeline 命名清楚,再把它挂到现有 `RDEPUBDTCoreTextRenderer` 和 `RDEPUBTextBookBuilder` 上。
+- 任何涉及样式 precedence 的描述,都要写出 `default / replace / dark / epub / user` 的顺序,而不是只说“增加 CSS 支持”。
+- 资源验证优先写 chapter baseURL 和相对资源引用,不要只验证纯文本章节。
+- 如果要提到 WebView helper,只能作为“不要复用”的反例。
diff --git a/.planning/phases/02-typesetter-css/02-RESEARCH.md b/.planning/phases/02-typesetter-css/02-RESEARCH.md
new file mode 100644
index 0000000..641534e
--- /dev/null
+++ b/.planning/phases/02-typesetter-css/02-RESEARCH.md
@@ -0,0 +1,177 @@
+# Phase 2: 重构 typesetter 与 CSS 分层 - Research
+
+**Researched:** 2026-05-21
+**Domain:** iOS EPUB reader architecture / native typesetting and chapter-scoped CSS layering
+**Confidence:** HIGH
+
+
+## User Constraints (from CONTEXT.md)
+
+No user constraints - all decisions at the agent's discretion.
+
+
+
+## Architectural Responsibility Map
+
+| Capability | Primary Tier | Secondary Tier | Rationale |
+|------------|-------------|----------------|-----------|
+| 章节 HTML 预处理与 CSS 层级组装 | Browser/Client | EPUBTextRendering | 这是 Phase 2 的主战场,必须在 native reflowable 路径里完成。 |
+| 章节级 baseURL、资源寻址与样式注入 | Browser/Client | EPUBCore | `RDEPUBTextBookBuilder` 负责提供 chapter boundary 和 baseURL,`RDEPUBResourceResolver` 负责资源定位。 |
+| 章节 HTML to `NSAttributedString` 导入 | Browser/Client | EPUBTextRendering | `RDEPUBDTCoreTextRenderer` 仍是 DTCoreText 入口,但不能再只依赖一个扁平 option bag。 |
+| 主题、字号、行高、背景色等用户输入 | EPUBUI | Browser/Client | `RDEPUBReaderConfiguration` / `RDEPUBPreferences` 仍是用户态入口,但它们必须被转换成可叠加的样式层。 |
+| WebView 渲染与测量辅助 | Browser/Client | EPUBCore | `RDEPUBStyleSheetBuilder` 只服务 `WKWebView` 路径,不应直接搬到 native renderer 里。 |
+
+
+
+
+## Summary
+
+当前 native reflowable 路径已经有章节边界,但样式输入仍然太薄。`RDEPUBTextBookBuilder` 只负责取出 spine HTML、给出 chapter baseURL,然后把原始 HTML 直接交给 `RDEPUBDTCoreTextRenderer`。后者再用 `DTHTMLAttributedStringBuilder` 加一个小型 option bag 导入,最后靠 `normalizeReadingAttributes` 做字体和行距修正。这种结构能跑通基础排版,但不具备 WXRead 风格的显式 CSS 分层,也没有把章节资源、publication 级样式和用户样式拆成可验证的输入层。
+
+Phase 2 的正确切入点不是重写 parser,也不是把 `RDEPUBStyleSheetBuilder` 从 WebView 路径直接复用到 native 路径。更稳妥的做法是:在 `EPUBTextRendering` 内部建立一个明确的 stylesheet / preprocessing pipeline,先把章节 HTML 规范化、补齐 baseURL 和资源引用上下文,再按 `default / replace / dark / epub / user` 的顺序合成样式层,最后把合成后的 HTML/CSS 交给 DTCoreText。
+
+这个阶段的目标是把 renderer 输入从“`DTCoreText` 默认 builder + 少量 options”升级成“章节上下文 + 样式层 + 资源上下文 + 规范化 HTML”。这样后续 Phase 3 才能在不重做入口的前提下,引入更重的页面级属性和分页语义。
+
+**Primary recommendation:** Phase 2 应先把 native stylesheet pipeline 和 chapter preprocessing 固化在 `EPUBTextRendering`,然后再把 `RDEPUBDTCoreTextRenderer` / `RDEPUBTextBookBuilder` 接到这个 pipeline 上,保持 `RDReaderView` 和 `WKWebView` 路径不动。
+
+
+
+## Standard Stack
+
+### Core
+| Library | Version | Purpose | Why Standard |
+|---------|---------|---------|--------------|
+| UIKit | iOS 15+ SDK | Reader UI and style input | 当前 reader 配置、主题与视图层仍然由 UIKit 提供。 |
+| Foundation | System | HTML normalization, URL handling, string processing | 章节级 baseURL、样式拼接与资源路径修正都依赖它。 |
+| CoreText | System | Pagination fallback and text measurement | Native book builder 仍需要它做后续分页验证。 |
+| DTCoreText | 1.6.28 | HTML/CSS to `NSAttributedString` | Phase 2 的目标是把它从“简单导入器”提升成“可配置的章节 typesetter”。 |
+| WebKit | System | Fixed-layout / interactive EPUB only | 只作为对照边界存在,不进入 Phase 2 的 native 分层。 |
+
+### Supporting
+| Library | Version | Purpose | When to Use |
+|---------|---------|---------|-------------|
+| ZIPFoundation | 0.9.20 | EPUB archive/resource access | 资源是否能被 chapter baseURL 正确解析,需要它提供底层文件。 |
+| DTFoundation | 1.7.19 | DTCoreText support | DTCoreText 解析 HTML / 附件 / 图片资源仍依赖它。 |
+
+### Alternatives Considered
+| Instead of | Could Use | Tradeoff |
+|------------|-----------|----------|
+| Explicit native CSS layering | Continue passing a small `dtOptions` bag | 不能表达 `default / replace / dark / epub / user` 的层级关系,也难验证覆盖顺序。 |
+| Chapter-scoped preprocessing in `EPUBTextRendering` | Reuse `RDEPUBStyleSheetBuilder` from WebView path | 会把 WebView 测量语义带进 native renderer,职责混淆。 |
+| DTCoreText-based native evolution | Replacing DTCoreText with a custom HTML parser | 成本和风险都过高,会偏离当前 roadmap 的“直接重构旧引擎”原则。 |
+
+**Installation:**
+```bash
+pod install
+```
+
+
+
+## Architecture Patterns
+
+### System Architecture Diagram
+
+```text
+RDEPUBReaderConfiguration / RDEPUBPreferences
+ -> RDEPUBTextRenderStyle
+ -> RDEPUBTextBookBuilder
+ -> chapter HTML + baseURL + resource context
+ -> native stylesheet / preprocessing pipeline
+ -> RDEPUBDTCoreTextRenderer
+ -> DTHTMLAttributedStringBuilder
+ -> NSAttributedString
+ -> RDEPUBTextBook / RDEPUBTextPage
+```
+
+### Recommended Project Structure
+
+```text
+Sources/RDReaderView/
+├── EPUBCore/ # Parser, publication, resource resolution, web pagination
+├── EPUBTextRendering/ # Native reflowable rendering, preprocessing, pagination
+├── EPUBUI/ # Reader configuration, theme, controller wiring
+└── RDReaderView.swift # Stable page container and gestures
+```
+
+### Pattern 1: Keep chapter context explicit
+**What:** Every chapter render should know its `href`, `baseURL`, publication-scoped styles, and user style inputs before DTCoreText import begins.
+**When to use:** When a renderer must resolve relative CSS/image/font URLs reliably.
+**Example:** `RDEPUBTextBookBuilder` already knows chapter `href` and `baseURL`; Phase 2 should make that context first-class instead of implicit.
+
+### Pattern 2: Make CSS precedence explicit
+**What:** Build a deterministic layer order: `default -> replace -> dark -> epub -> user`.
+**When to use:** When theme, publication, and user preferences can all change the same visual property.
+**Example:** Dark theme text/background colors should be the highest-priority visible override, while EPUB resources remain below user settings.
+
+### Pattern 3: Separate preprocessing from pagination
+**What:** Normalize HTML and resolve resources before chapter import; keep pagination logic in the next stage.
+**When to use:** When `RDEPUBDTCoreTextRenderer` must remain responsible for HTML-to-attributed-string conversion, not page slicing.
+**Example:** Phase 2 should leave `ss_pageRanges(size:)` untouched and focus only on renderer input quality.
+
+### Anti-Patterns to Avoid
+- **Using `RDEPUBStyleSheetBuilder` as the native stylesheet engine:** It is a WebView measurement helper and already encodes column/layout semantics that do not belong in native DTCoreText import.
+- **Treating `RDEPUBTextRenderStyle` as the final style model:** It currently carries font, line spacing, and colors, but Phase 2 needs a richer chapter-scoped stylesheet pipeline around it.
+- **Pushing CSS layering into `RDReaderView`:** The page container should remain a consumer of rendered pages, not a CSS assembly point.
+
+
+
+
+## Don't Hand-Roll
+
+| Problem | Don't Build | Use Instead | Why |
+|---------|-------------|-------------|-----|
+| HTML parsing | A brand-new EPUB HTML parser | DTCoreText builder extension points | The repo already depends on DTCoreText for native text rendering. |
+| Resource resolution | Ad-hoc string replacement for relative URLs | `RDEPUBResourceResolver` and `baseURL` context | Resource resolution must remain chapter-scoped and deterministic. |
+| Theme plumbing | Copying WebView CSS injection logic into native renderer | Reader configuration + native stylesheet layers | Native renderer should own native style semantics. |
+| Pagination semantics | Page slicing changes in Phase 2 | Keep current `ss_pageRanges(size:)` boundary | Phase 3 owns the heavy pagination rewrite. |
+
+**Key insight:** Phase 2 should make chapter input richer, not make pagination heavier. The win is in the renderer input contract, not in a new page model yet.
+
+
+
+## Common Pitfalls
+
+### Pitfall 1: Reusing the WebView stylesheet helper too early
+**What goes wrong:** The native path accidentally inherits WebView-only column and viewport semantics.
+**Why it happens:** `RDEPUBStyleSheetBuilder` looks like a ready-made stylesheet utility, but it is purpose-built for `WKWebView`.
+**How to avoid:** Build a separate native stylesheet helper under `EPUBTextRendering`.
+
+### Pitfall 2: Letting style precedence stay implicit
+**What goes wrong:** Theme, user preferences, and EPUB CSS fight each other with no stable priority.
+**Why it happens:** `dtOptions` is too small to represent the full cascade.
+**How to avoid:** Make layer ordering an explicit API and document it in the renderer contract.
+
+### Pitfall 3: Breaking resource URLs while normalizing HTML
+**What goes wrong:** Images, linked stylesheets, and fonts stop loading once HTML is rewritten.
+**Why it happens:** Preprocessing can easily strip or relocate nodes without preserving `baseURL`.
+**How to avoid:** Keep chapter `baseURL` and resource resolution visible in the pipeline and verify with sample EPUBs.
+
+
+
+
+## State of the Art (2024-2025)
+
+| Old Approach | Current Approach | When Changed | Impact |
+|--------------|------------------|--------------|--------|
+| Flat HTML import with a small option bag | Explicit chapter-scoped preprocessing plus layered CSS | Already implied by WXRead reference material | Needed to support publication/user/theme overrides in a predictable order. |
+| Theme colors applied after import | Style inputs assembled before or during import | Already visible in current repo boundary | Reduces post-processing ambiguity and makes resource/style verification easier. |
+| One-size-fits-all renderer input | Chapter context with separate resource/style concerns | Required by Phase 2 scope | Makes baseURL and CSS resolution testable. |
+
+**New tools/patterns to consider:**
+- Chapter-scoped stylesheet assembly: keeps `href`, `baseURL`, and style precedence together.
+- Pre-import HTML normalization: a safe place to inject style scaffolding without changing page slicing.
+
+**Deprecated/outdated:**
+- “Simple DTCoreText renderer + post-hoc normalization” as a long-term design.
+- “Borrow WebView CSS helpers for native rendering” as a direct implementation strategy.
+
+
+
+
+## Open Questions
+
+- Should `default` and `replace` layers be generated from engine code, or should some defaults live in bundled EPUB/native CSS assets?
+- Where should publication-scoped CSS be sourced from first: OPF manifest, linked chapter CSS files, or a normalized in-memory layer?
+- Do we need a dedicated native context type for chapter render input, or can `RDEPUBTextBookBuilder` carry all inputs through existing method parameters?
+- How much of the current `RDEPUBTextRenderStyle` should remain public API versus moving into an internal stylesheet model?
+
diff --git a/.planning/phases/02-typesetter-css/02-VALIDATION.md b/.planning/phases/02-typesetter-css/02-VALIDATION.md
new file mode 100644
index 0000000..13c9c20
--- /dev/null
+++ b/.planning/phases/02-typesetter-css/02-VALIDATION.md
@@ -0,0 +1,76 @@
+---
+phase: 2
+slug: typesetter-css
+status: draft
+nyquist_compliant: false
+wave_0_complete: false
+created: 2026-05-21
+---
+
+# Phase 2 — Validation Strategy
+
+> Per-phase validation contract for feedback sampling during execution.
+
+---
+
+## Test Infrastructure
+
+| Property | Value |
+|----------|-------|
+| **Framework** | shell assertions + Xcode build smoke + demo sample EPUB spot-checks |
+| **Config file** | none — current repo has no dedicated XCTest target for this phase |
+| **Quick run command** | `test -f .planning/phases/02-typesetter-css/02-RESEARCH.md && test -f .planning/phases/02-typesetter-css/02-PATTERNS.md` |
+| **Full suite command** | `xcodebuild -workspace ReadViewDemo/ReadViewDemo.xcworkspace -scheme ReadViewDemo -destination 'platform=iOS Simulator,name=iPhone 15' build` |
+| **Estimated runtime** | ~60-90 seconds |
+
+---
+
+## Sampling Rate
+
+- **After every task commit:** Run the matching `rg` assertions for the touched plan artifact and confirm the intended source files still name the new pipeline entry points.
+- **After every plan wave:** Run `xcodebuild -workspace ReadViewDemo/ReadViewDemo.xcworkspace -scheme ReadViewDemo -destination 'platform=iOS Simulator,name=iPhone 15' build`.
+- **Before `$gsd-verify-work`:** Build smoke must be green, the three phase docs must exist, and sample EPUBs must still load in the demo app.
+- **Max feedback latency:** 90 seconds
+
+---
+
+## Per-Task Verification Map
+
+| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
+|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------|
+| 02-01-01 | 01 | 1 | REND-02 | — | CSS layering helper has explicit `default / replace / dark / epub / user` precedence and chapter preprocessing hooks | source | `rg -n "default|replace|dark|epub|user|baseURL|preprocess|stylesheet" Sources/RDReaderView/EPUBTextRendering` | ❌ W0 | ⬜ pending |
+| 02-02-01 | 02 | 2 | REND-01, REND-02 | — | `RDEPUBDTCoreTextRenderer` consumes chapter context instead of a flat option bag, and still preserves `RDEPUBTextBookBuilder` chapter boundaries | source | `rg -n "RDEPUBDTCoreTextRenderer|RDEPUBTextBookBuilder|chapter context|stylesheet|DTHTMLAttributedStringBuilder" Sources/RDReaderView/EPUBTextRendering Sources/RDReaderView/EPUBUI` | ❌ W0 | ⬜ pending |
+| 02-03-01 | 03 | 3 | REND-01, REND-02 | — | Sample EPUBs resolve chapter CSS/image/baseURL references under the new pipeline | smoke/manual | `rg -n "resource|baseURL|css|image|stylesheet" .planning/phases/02-typesetter-css/02-03-PLAN.md` | ❌ W0 | ⬜ pending |
+
+*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*
+
+---
+
+## Wave 0 Requirements
+
+- [ ] No new test target required for Phase 2 planning work.
+- [ ] Executor must confirm `ReadViewDemo/ReadViewDemo.xcworkspace` and `ReadViewDemo` scheme still open/build before closing the phase.
+- [ ] Sample books in `ReadViewDemo/ReadViewDemo/book/` remain the regression corpus for manual spot-checks.
+
+---
+
+## Manual-Only Verifications
+
+| Behavior | Requirement | Why Manual | Test Instructions |
+|----------|-------------|------------|-------------------|
+| CSS layer precedence is correct in actual rendered chapters | REND-02 | Must compare visible output, not just source strings | Open a sample reflowable EPUB and confirm theme/user/publication styles apply in the intended order. |
+| Relative CSS/image/baseURL resolution is intact | REND-01, REND-02 | Resource loading failures are easiest to see in the demo app | Open EPUBs with images and linked CSS and verify they still render after preprocessing. |
+| WebView-only style helpers were not repurposed | REND-01, REND-02 | This is a boundary judgement, not a unit assertion | Inspect the final renderer wiring and confirm `RDEPUBStyleSheetBuilder` remains on the WebView path only. |
+
+---
+
+## Validation Sign-Off
+
+- [ ] All tasks have `` verify or Wave 0 dependencies
+- [ ] Sampling continuity: no 3 consecutive tasks without automated verify
+- [ ] Wave 0 covers all MISSING references
+- [ ] No watch-mode flags
+- [ ] Feedback latency < 90s
+- [ ] `nyquist_compliant: true` set in frontmatter
+
+**Approval:** pending
diff --git a/.planning/phases/02-typesetter-css/02-VERIFICATION.md b/.planning/phases/02-typesetter-css/02-VERIFICATION.md
new file mode 100644
index 0000000..8fa6b36
--- /dev/null
+++ b/.planning/phases/02-typesetter-css/02-VERIFICATION.md
@@ -0,0 +1,70 @@
+---
+phase: 02-typesetter-css
+verified: 2026-05-22T04:14:30Z
+status: passed
+score: 3/3 must-haves verified
+---
+
+# Phase 02: Typesetter CSS Verification Report
+
+**Phase Goal:** 在旧引擎 native reflowable 路径中建立 WXRead 风格的 chapter preprocessing / CSS 分层输入,并验证真实 EPUB 资源解析没有断裂。
+**Verified:** 2026-05-22T04:14:30Z
+**Status:** passed
+
+## Goal Achievement
+
+### Observable Truths
+
+| # | Truth | Status | Evidence |
+|---|-------|--------|----------|
+| 1 | reflowable renderer 的章节输入不再只是 DTCoreText option bag | ✓ VERIFIED | [RDEPUBTextRenderer.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift) 新增 `RDEPUBTextChapterContext` / `RDEPUBTextChapterRenderRequest`,`RDEPUBTextBookBuilder` 在 chapter boundary 组装 request。 |
+| 2 | CSS 五层顺序 `default / replace / dark / epub / user` 在 native path 中显式落地 | ✓ VERIFIED | [RDEPUBTextRendererSupport.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRendererSupport.swift) 的 `makeStyleSheetLayers` 固定了分层顺序,并通过 `injectStyleTag` 注入到 chapter HTML。 |
+| 3 | 章节级 baseURL、linked CSS、图片资源在真实样本验证中可解析 | ✓ VERIFIED | [RDEPUBResourceResolver.swift](/Users/shen/Work/Code/ReadViewSDK/Sources/RDReaderView/EPUBCore/RDEPUBResourceResolver.swift) 扩展了 chapter-relative 标准化;demo runtime log 记录 `[ReadViewDemo] EPUB 资源验证:2/2 通过`。 |
+
+**Score:** 3/3 truths verified
+
+### Required Artifacts
+
+| Artifact | Expected | Status | Details |
+|----------|----------|--------|---------|
+| `.planning/phases/02-typesetter-css/02-01-SUMMARY.md` | preprocessing / stylesheet layering 执行总结 | ✓ EXISTS + SUBSTANTIVE | 记录 native stylesheet pipeline 与关键决策。 |
+| `.planning/phases/02-typesetter-css/02-02-SUMMARY.md` | renderer wiring 执行总结 | ✓ EXISTS + SUBSTANTIVE | 记录 request-based renderer contract 与 resolver 变更。 |
+| `.planning/phases/02-typesetter-css/02-03-SUMMARY.md` | sample EPUB validation 总结 | ✓ EXISTS + SUBSTANTIVE | 记录 demo corpus 验证与运行时结果。 |
+
+**Artifacts:** 3/3 verified
+
+### Key Link Verification
+
+| From | To | Via | Status | Details |
+|------|----|----|--------|---------|
+| `RDEPUBTextBookBuilder` | `RDEPUBDTCoreTextRenderer` | chapter render request | ✓ WIRED | builder 组装 `RDEPUBTextChapterRenderRequest` 并透传给 renderer。 |
+| stylesheet preprocessing | resource normalization | `RDEPUBResourceResolver` | ✓ WIRED | linked CSS 与 image diagnostics 使用 chapter-relative resolver API。 |
+| demo validation | real sample corpus | `ReadViewDemo/ReadViewDemo/book/` | ✓ WIRED | demo 启动时后台扫描 EPUB corpus,并输出 `2/2` 通过结果。 |
+
+**Wiring:** 3/3 connections verified
+
+## Requirements Coverage
+
+| Requirement | Status | Notes |
+|-------------|--------|-------|
+| REND-02 | ✓ SATISFIED | CSS 五层分层已在 native renderer 入口落地。 |
+| REND-01 | ◐ PARTIAL | 本阶段完成 renderer 输入与资源语义重构;页面级元数据与复杂分页能力留给 Phase 3。 |
+
+## Human Verification Required
+
+- 若需要视觉确认具体某本书的版式细节,仍建议在 simulator 中手工打开对应 EPUB 做 spot-check。
+
+## Gaps Summary
+
+**No blocking gaps found.** Phase 2 goal achieved. Ready for Phase 3 planning.
+
+## Verification Metadata
+
+**Verification approach:** code inspection + simulator build/run + sample corpus log verification
+**Automated checks:** `rg` contract checks, simulator build, demo runtime validation summary
+**Human checks required:** optional visual spot-check only
+**Total verification time:** ~8 min
+
+---
+*Verified: 2026-05-22T04:14:30Z*
+*Verifier: inline executor*
diff --git a/.planning/phases/03-page-metadata-pagination/03-01-PLAN.md b/.planning/phases/03-page-metadata-pagination/03-01-PLAN.md
new file mode 100644
index 0000000..4ceadf7
--- /dev/null
+++ b/.planning/phases/03-page-metadata-pagination/03-01-PLAN.md
@@ -0,0 +1,105 @@
+---
+phase: 03-page-metadata-pagination
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift
+autonomous: true
+requirements:
+ - REND-03
+user_setup: []
+must_haves:
+ truths:
+ - `RDEPUBTextPage` / `RDEPUBTextChapter` gain explicit page metadata hooks for blocks, attachments, and page-edge reasoning.
+ - `pageStartOffset`, `pageEndOffset`, chapter text continuity, and fragment offsets remain first-class compatibility fields.
+ - new metadata is serializable or structurally accessible from current native rendering models rather than hidden inside temporary layout locals.
+ artifacts:
+ - .planning/phases/03-page-metadata-pagination/03-01-SUMMARY.md
+ key_links:
+ - `RDEPUBTextOffsetRangeInfo` remains compatible with absolute chapter offsets.
+ - `RDEPUBTextBookBuilder` stays the owner of chapter/page assembly.
+---
+
+
+定义页面级 attributed string 元数据与自定义属性键,为 Phase 3 的复杂分页器建立稳定数据契约。
+
+Purpose: 先把“页面语义到底长什么样”固定下来,再让 paginator 和验证围绕这套契约扩展,而不是在分页逻辑里临时发明结构。
+Output: richer page/chapter metadata models、attribute key definitions,以及保持 offset continuity 的序列化/传递路径。
+
+
+
+@$HOME/.codex/get-shit-done/workflows/execute-plan.md
+@$HOME/.codex/get-shit-done/templates/summary.md
+
+
+
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/REQUIREMENTS.md
+@.planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md
+@.planning/phases/02-typesetter-css/02-RESEARCH.md
+@.planning/phases/03-page-metadata-pagination/03-RESEARCH.md
+@.planning/phases/03-page-metadata-pagination/03-PATTERNS.md
+@Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift
+@Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
+
+
+
+
+
+ Task 1: 定义页面元数据与 attributed string 属性键
+ Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
+ Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift, Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
+ 在现有 native text 模型旁定义 page metadata 契约和 attributed string 语义键,至少覆盖 block boundary、attachment/image semantic、page break reason、future page style hook 这几类能力;把它们放在 `RDEPUBReadingModels.swift` 或与之紧邻的 native rendering 模型里,并保持 `RDEPUBTextOffsetRangeInfo` 继续基于绝对 chapter offsets 工作。不要删除或弱化 `pageStartOffset`、`pageEndOffset`、`fragmentOffsets`、`href` 等现有兼容字段。
+
+ - `Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift` contains new page metadata types or fields for block / attachment / page break semantics
+ - `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift` or adjacent rendering models contain explicit native metadata/attribute-key definitions
+ - `RDEPUBTextOffsetRangeInfo` remains present and still serializes absolute offset ranges
+
+ rg -n "metadata|attachment|block|pageBreak|pageStartOffset|pageEndOffset|fragmentOffsets" Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift
+ native text pipeline now has an explicit metadata vocabulary that later pagination and validation steps can consume.
+
+
+
+ Task 2: 扩展 chapter/page model 以承接新语义但保留 offset continuity
+ Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift
+ Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift
+ 把 `RDEPUBTextChapter` 和 `RDEPUBTextPage` 扩展成可承接新 metadata 的模型,同时保留 `RDEPUBTextBook.pageNumber(for:)`、`location(forPageNumber:)`、search/highlight 所依赖的 offset continuity。若需要新增 page-slice carrier(例如 layout snapshot 或 page metadata payload),它必须最终落回 `RDEPUBTextPage`,不能绕开现有 `RDEPUBTextBook` 契约。
+
+ - `RDEPUBTextPage` includes explicit metadata payloads or fields beyond bare range/offsets
+ - `RDEPUBTextBook.pageNumber(for:)` and `location(forPageNumber:)` still use offset-compatible fields
+ - source code still contains `pageStartOffset`, `pageEndOffset`, and `fragmentOffsets` after the refactor
+
+ rg -n "struct RDEPUBTextPage|struct RDEPUBTextChapter|pageStartOffset|pageEndOffset|fragmentOffsets|metadata" Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ the page model is richer, but all current consumers still have a stable offset-based contract to target.
+
+
+
+
+
+Before declaring plan complete:
+- [ ] `rg -n "metadata|attachment|block|pageBreak" Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift Sources/RDReaderView/EPUBTextRendering`
+- [ ] `rg -n "pageStartOffset|pageEndOffset|fragmentOffsets" Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift`
+- [ ] no changes route page presentation around `RDEPUBTextBook`
+
+
+
+
+- All tasks completed
+- All verification checks pass
+- No compatibility field needed by highlight/search/navigation is removed
+- Phase 3.2 can consume the metadata contract without redefining the page model again
+
+
+
+
diff --git a/.planning/phases/03-page-metadata-pagination/03-01-SUMMARY.md b/.planning/phases/03-page-metadata-pagination/03-01-SUMMARY.md
new file mode 100644
index 0000000..42b9e34
--- /dev/null
+++ b/.planning/phases/03-page-metadata-pagination/03-01-SUMMARY.md
@@ -0,0 +1,20 @@
+# 03-01 Summary
+
+## Outcome
+
+Wave 1 landed the page metadata contract without breaking the existing offset-based book model.
+
+- Added `RDEPUBTextPageBreakReason`, `RDEPUBTextAttachmentKind`, and `RDEPUBTextPageMetadata` in `RDEPUBReadingModels.swift`
+- Added attributed-string keys in `RDEPUBTextRenderer.swift` for block range/index, fragment id, and attachment kind
+- Extended `RDEPUBTextPage` with `metadata`
+- Extended `RDEPUBTextChapter` with `pageBreakReasons`
+- Tagged normalized attributed content in `RDEPUBTextRendererSupport.normalizeReadingAttributes` so later pagination can inspect block and attachment semantics
+
+## Compatibility
+
+- `pageStartOffset`, `pageEndOffset`, `contentRange`, `fragmentOffsets`, and chapter text continuity remain intact
+- Existing `RDEPUBTextBook` page lookup and location mapping continue to operate on absolute chapter offsets
+
+## Notes
+
+This wave deliberately extended the current book contract instead of replacing it. That kept highlight/search/navigation consumers stable while creating a place to land richer pagination semantics in Wave 2.
diff --git a/.planning/phases/03-page-metadata-pagination/03-02-PLAN.md b/.planning/phases/03-page-metadata-pagination/03-02-PLAN.md
new file mode 100644
index 0000000..fe0847d
--- /dev/null
+++ b/.planning/phases/03-page-metadata-pagination/03-02-PLAN.md
@@ -0,0 +1,106 @@
+---
+phase: 03-page-metadata-pagination
+plan: 02
+type: execute
+wave: 2
+depends_on:
+ - "03-01"
+files_modified:
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextLayouter.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextLayoutFrame.swift
+autonomous: true
+requirements:
+ - REND-03
+ - REND-04
+user_setup: []
+must_haves:
+ truths:
+ - pagination is no longer implemented as only `CTFrameGetVisibleStringRange` range slicing.
+ - layouter/layout-frame responsibilities live inside `EPUBTextRendering`, not in reader UI classes.
+ - stronger page edges still resolve to stable `RDEPUBTextPage` offsets and chapter continuity.
+ artifacts:
+ - .planning/phases/03-page-metadata-pagination/03-02-SUMMARY.md
+ key_links:
+ - `RDEPUBTextPaginationSupport` remains the entry point for native pagination work.
+ - `RDEPUBTextBookBuilder` still assembles final pages and chapters.
+---
+
+
+在旧引擎基础上重构分页器,引入内部 layouter / layout-frame 语义,并把纯 visible-range slicing 升级为更强的页面边界控制。
+
+Purpose: 让 native paginator 能表达 block / image / attachment 的页面边界语义,同时不破坏现有 page model 与 chapter offset continuity。
+Output: stronger paginator implementation, internal layouter/frame types, and book-builder wiring onto the new pagination path.
+
+
+
+@$HOME/.codex/get-shit-done/workflows/execute-plan.md
+@$HOME/.codex/get-shit-done/templates/summary.md
+
+
+
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/REQUIREMENTS.md
+@.planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md
+@.planning/phases/03-page-metadata-pagination/03-RESEARCH.md
+@.planning/phases/03-page-metadata-pagination/03-PATTERNS.md
+@.planning/phases/03-page-metadata-pagination/03-01-PLAN.md
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+@Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift
+@Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
+
+
+
+
+
+ Task 1: 引入 layouter / layout-frame 语义并升级分页入口
+ Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextLayouter.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextLayoutFrame.swift
+ Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, .planning/phases/01-current-engine-boundaries/01-refactor-entry-strategy.md
+ 把当前 `ss_pageRanges(size:)` 的纯 range slicing 入口升级成内部 layouter / layout-frame 驱动的分页路径;新路径至少要能表达 page break reason、block boundary avoidance、image/attachment boundary handling 这三类语义。新增类型应放在 `EPUBTextRendering` 内部,例如 `RDEPUBTextLayouter` / `RDEPUBTextLayoutFrame`,并由 `RDEPUBTextPaginationSupport` 调用,而不是把复杂分页逻辑堆到 UI controller 里。
+
+ - `Sources/RDReaderView/EPUBTextRendering` contains layouter/frame types or equivalent internal pagination structures
+ - paginator code contains semantics beyond plain `CTFrameGetVisibleStringRange` slicing
+ - source tree does not move pagination logic into `RDEPUBReaderController` or `RDEPUBTextContentView`
+
+ rg -n "Layouter|LayoutFrame|pageBreak|attachment|avoid|CTFrameGetVisibleStringRange|CTTypesetter" Sources/RDReaderView/EPUBTextRendering
+ native pagination now has a real internal layout vocabulary rather than one visible-range helper.
+
+
+
+ Task 2: 把更强分页结果接回 `RDEPUBTextBookBuilder` 与现有页模型
+ Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift, Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
+ 让 `RDEPUBTextBookBuilder` 消费新分页结果并把 page-level metadata 落到 `RDEPUBTextPage` / `RDEPUBTextChapter` 上,同时保持 `pageStartOffset` / `pageEndOffset`、chapter order、fragmentOffsets 和 search/highlight overlap 逻辑不变。需要时可以在 builder 内转换 layouter output,但最终输出仍必须是当前 `RDEPUBTextBook` 契约,而不是新的 UI-only page list。
+
+ - `RDEPUBTextBookBuilder` constructs pages from the upgraded pagination output rather than raw `ss_pageRanges(size:)` only
+ - page model assembly still emits `RDEPUBTextBook(chapters:pages:)`
+ - search/highlight/navigation dependent fields remain present in the built pages
+
+ rg -n "RDEPUBTextBook\\(|pageStartOffset|pageEndOffset|fragmentOffsets|metadata|layout" Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ the richer paginator is wired back into the existing native book contract and remains consumable by later reader phases.
+
+
+
+
+
+Before declaring plan complete:
+- [ ] `test -f Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift`
+- [ ] `rg -n "Layouter|LayoutFrame|pageBreak|attachment|avoid" Sources/RDReaderView/EPUBTextRendering`
+- [ ] `rg -n "RDEPUBTextBook\\(|pageStartOffset|pageEndOffset|fragmentOffsets" Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift`
+
+
+
+
+- All tasks completed
+- All verification checks pass
+- paginator semantics are meaningfully stronger than pure visible-range slicing
+- output still flows through the existing native text book/page model
+
+
+
+
diff --git a/.planning/phases/03-page-metadata-pagination/03-02-SUMMARY.md b/.planning/phases/03-page-metadata-pagination/03-02-SUMMARY.md
new file mode 100644
index 0000000..0bf6f3a
--- /dev/null
+++ b/.planning/phases/03-page-metadata-pagination/03-02-SUMMARY.md
@@ -0,0 +1,21 @@
+# 03-02 Summary
+
+## Outcome
+
+Wave 2 replaced the range-only pagination path with an internal layouter/frame vocabulary inside `EPUBTextRendering`.
+
+- Added `RDEPUBTextLayouter.swift`
+- Added `RDEPUBTextLayoutFrame.swift`
+- Updated `RDEPUBTextPaginationSupport.swift` so `ss_pageRanges(size:)` now delegates to `rd_paginatedFrames(size:fragmentOffsets:)`
+- Updated `RDEPUBTextBookBuilder` and `RDPlainTextBookBuilder` to build pages from layout frames instead of bare `NSRange` slices
+
+## New Semantics
+
+- Page breaks now carry one of `chapterEnd`, `frameLimit`, `blockBoundary`, or `attachmentBoundary`
+- Layout frames capture block-range context, attachment ranges/kinds, trailing fragment id, and page-edge diagnostics
+- The layouter still uses CoreText measurement, but page boundaries are adjusted with block/attachment-aware rules before serialization back into `RDEPUBTextPage`
+
+## Compatibility
+
+- Final output is still `RDEPUBTextBook(chapters: pages:)`
+- `pageStartOffset` / `pageEndOffset` remain the compatibility key for search, highlights, and later reader reintegration
diff --git a/.planning/phases/03-page-metadata-pagination/03-03-PLAN.md b/.planning/phases/03-page-metadata-pagination/03-03-PLAN.md
new file mode 100644
index 0000000..7cc39d9
--- /dev/null
+++ b/.planning/phases/03-page-metadata-pagination/03-03-PLAN.md
@@ -0,0 +1,106 @@
+---
+phase: 03-page-metadata-pagination
+plan: 03
+type: execute
+wave: 3
+depends_on:
+ - "03-02"
+files_modified:
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift
+ - Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
+ - ReadViewDemo/ReadViewDemo/ViewController.swift
+autonomous: true
+requirements:
+ - REND-03
+ - REND-04
+user_setup: []
+must_haves:
+ truths:
+ - sample EPUBs with images or large blocks can prove stronger page-edge semantics through real diagnostics, not only visual inspection.
+ - offset-based highlight/search compatibility survives the stronger paginator.
+ - the current demo app remains the validation entry point.
+ artifacts:
+ - .planning/phases/03-page-metadata-pagination/03-03-SUMMARY.md
+ key_links:
+ - `ReadViewDemo/ReadViewDemo/book/` remains the regression corpus.
+ - `RDEPUBTextContentView` still derives overlaps from `pageStartOffset` / `pageEndOffset`.
+---
+
+
+用真实样本验证复杂块元素、图片与分页边界控制在新分页器下可工作,并确认 offset-compatible highlight/search 行为没有被破坏。
+
+Purpose: 证明 Phase 3 的 richer metadata 和 stronger paginator 不只是结构升级,而是能在真实 EPUB 上给出可解释、可回归的分页结果。
+Output: demo-visible diagnostics, sample-book spot-check path, and source assertions around metadata + offset compatibility.
+
+
+
+@$HOME/.codex/get-shit-done/workflows/execute-plan.md
+@$HOME/.codex/get-shit-done/templates/summary.md
+
+
+
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/03-page-metadata-pagination/03-RESEARCH.md
+@.planning/phases/03-page-metadata-pagination/03-PATTERNS.md
+@.planning/phases/03-page-metadata-pagination/03-01-PLAN.md
+@.planning/phases/03-page-metadata-pagination/03-02-PLAN.md
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift
+@Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
+@ReadViewDemo/ReadViewDemo/ViewController.swift
+@ReadViewDemo/ReadViewDemo/book/
+
+
+
+
+
+ Task 1: 对真实 EPUB 暴露分页诊断并验证图片/块元素边界
+ Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift, ReadViewDemo/ReadViewDemo/ViewController.swift
+ Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift, ReadViewDemo/ReadViewDemo/ViewController.swift, ReadViewDemo/ReadViewDemo/book/
+ 基于现有 demo 样本书,为 stronger paginator 增加最小但可观察的分页诊断输出,至少能报告 page metadata / page break reason / image-or-block boundary evidence 中的一部分,并用 `ReadViewDemo/ReadViewDemo/book/` 的 reflowable EPUB 验证复杂块元素和图片没有回退到不可解释的纯 slicing 行为。不要新增并行 reader shell。
+
+ - demo or runtime logs expose pagination diagnostics tied to the new metadata or page-break semantics
+ - source tree still uses `ReadViewDemo/ReadViewDemo/ViewController.swift` as the validation entry point
+ - `ReadViewDemo/ReadViewDemo/book` remains the sample corpus referenced by the validation path
+
+ rg -n "diagnostic|metadata|page break|attachment|image|block" Sources/RDReaderView/EPUBTextRendering ReadViewDemo/ReadViewDemo
+ sample books can demonstrate that new pagination semantics are active and inspectable.
+
+
+
+ Task 2: 验证 offset-compatible 高亮/搜索页内映射没有退化
+ Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift
+ 确认 stronger paginator 产出的 page metadata 仍能支持 `RDEPUBTextContentView` 的 highlight/search overlap 计算和 `RDEPUBTextOffsetRangeInfo` 的绝对 offset 语义;如果需要,可补充最小的 source-level guard 或 diagnostics,但不要把兼容修复扩展成 Phase 4 的 reader reintegration。重点是验证 page-local presentation 依然能从 absolute chapter offsets 正确切出相对范围。
+
+ - `RDEPUBTextContentView` still computes overlaps from `pageStartOffset` / `pageEndOffset`
+ - `RDEPUBTextOffsetRangeInfo` remains decodable and tied to absolute chapter offsets
+ - no code path replaces highlight/search overlap logic with page-local-only identifiers
+
+ rg -n "pageStartOffset|pageEndOffset|RDEPUBTextOffsetRangeInfo|rangeInfo|searchState" Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift Sources/RDReaderView/EPUBTextRendering Sources/RDReaderView/EPUBCore
+ Phase 3 finishes with stronger pagination semantics and intact offset-based presentation compatibility.
+
+
+
+
+
+Before declaring plan complete:
+- [ ] `test -d ReadViewDemo/ReadViewDemo/book`
+- [ ] `rg -n "diagnostic|metadata|page break|attachment|image|block" Sources/RDReaderView ReadViewDemo/ReadViewDemo`
+- [ ] `rg -n "pageStartOffset|pageEndOffset|RDEPUBTextOffsetRangeInfo|rangeInfo" Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift Sources/RDReaderView/EPUBTextRendering Sources/RDReaderView/EPUBCore`
+
+
+
+
+- All tasks completed
+- All verification checks pass
+- sample books prove stronger paginator behavior is both active and inspectable
+- offset-based highlight/search presentation compatibility remains intact for Phase 4 handoff
+
+
+
+
diff --git a/.planning/phases/03-page-metadata-pagination/03-03-SUMMARY.md b/.planning/phases/03-page-metadata-pagination/03-03-SUMMARY.md
new file mode 100644
index 0000000..27ff054
--- /dev/null
+++ b/.planning/phases/03-page-metadata-pagination/03-03-SUMMARY.md
@@ -0,0 +1,19 @@
+# 03-03 Summary
+
+## Outcome
+
+Wave 3 exposed real-book pagination diagnostics and confirmed that offset-compatible highlight/search presentation stayed intact.
+
+- Added builder-level `RDEPUBTextChapterPaginationDiagnostic`
+- `RDEPUBTextBookBuilder` now records `lastBuildPaginationDiagnostics`
+- `ReadViewDemo/ViewController.swift` reports pagination evidence from sample EPUBs in UI/runtime logs
+- `RDEPUBTextContentView.swift` still computes highlight/search overlap from absolute `pageStartOffset` / `pageEndOffset`, now via an explicit helper
+
+## Demo Evidence
+
+Runtime log from the simulator:
+
+- `EPUB 资源验证:2/2 通过`
+- `分页诊断:宝山辽墓材料与释读 · 章节 10 · attachment 页 54 · semantic break 页 57 · reasons [attachmentBoundary:31, blockBoundary:26, frameLimit:17, chapterEnd:4] · page break: blockBoundary`
+
+This confirms the stronger paginator is active on real EPUB content and not silently falling back to pure visible-range slicing.
diff --git a/.planning/phases/03-page-metadata-pagination/03-PATTERNS.md b/.planning/phases/03-page-metadata-pagination/03-PATTERNS.md
new file mode 100644
index 0000000..84d9928
--- /dev/null
+++ b/.planning/phases/03-page-metadata-pagination/03-PATTERNS.md
@@ -0,0 +1,31 @@
+# Phase 3: 重构属性体系与复杂分页器 - Pattern Map
+
+## Goal
+
+为 Phase 3 执行提供“先看哪里、怎么保持兼容”的最短路径。这个阶段的关键不是改 reader UI,而是扩展 page metadata 并把 paginator 从纯 range slicing 升级为可表达页面语义的内部 layouter。
+
+## Planned Outputs
+
+| Planned file | Role | Primary evidence | Why this is the right analog |
+|--------------|------|------------------|------------------------------|
+| `.planning/phases/03-page-metadata-pagination/03-01-PLAN.md` | 页面级 attributed string 元数据与属性键实现计划 | `Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift`, `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift`, `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift` | 这些文件共同定义 page/chapter contract、range info、fragment offsets 与 renderer output。 |
+| `.planning/phases/03-page-metadata-pagination/03-02-PLAN.md` | 复杂分页器与 layouter/frame 重构计划 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift`, `RDEPUBTextBookBuilder.swift` | 当前所有分页都在这里;Phase 3 必须从这条路径升级,而不是另起 reader shell。 |
+| `.planning/phases/03-page-metadata-pagination/03-03-PLAN.md` | 复杂块元素/图片分页验证计划 | `ReadViewDemo/ReadViewDemo/book/*.epub`, `ReadViewDemo/ReadViewDemo/ViewController.swift`, `RDEPUBTextContentView.swift` | 需要真实样本和现有 demo 入口来证明分页语义变强但 offset compatibility 没坏。 |
+
+## Code Evidence Map
+
+| Concern | Closest source of truth | Evidence to extract |
+|---------|-------------------------|---------------------|
+| 当前 page model 的边界 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift` | `RDEPUBTextPage` / `RDEPUBTextChapter` 当前只有 offsets、ranges、fragmentOffsets。 |
+| 当前 paginator 的真实实现 | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextPaginationSupport.swift` | `CTFramesetterCreateFrame` + `CTFrameGetVisibleStringRange` 的纯 slicing 逻辑。 |
+| 位置/导航如何依赖 offsets | `Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift` | `pageIndex(for:)`、pending navigation、href/progression 到 page 的映射。 |
+| 高亮/选区如何依赖 offsets | `Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift`, `Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift` | `RDEPUBTextOffsetRangeInfo` 与 `pageStartOffset/pageEndOffset` 的 overlap 计算。 |
+| 搜索如何依赖 chapter text continuity | `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift` | chapter-level attributed string 搜索 + rangeLocation/rangeLength。 |
+| 复杂块元素/图片验证入口 | `ReadViewDemo/ReadViewDemo/book/`, `ReadViewDemo/ReadViewDemo/ViewController.swift` | 现有 demo 与样本 corpus 已经是可运行回归入口。 |
+
+## Writing Guidance
+
+- 计划必须把 “metadata first, paginator second, validation third” 的顺序写清楚,避免把所有变更压进一个 plan。
+- 任何新分页语义都要同时写出 “增强点” 和 “必须保留的 offset invariant”。
+- 如果引入内部 layouter / layout-frame 类型,职责要落在 `EPUBTextRendering` 内部,不要扩散到 `RDEPUBReaderController`。
+- 验证计划除了 demo/run,还要有源代码断言,确保 metadata 与 page-edge reason 真正落盘到模型里。
diff --git a/.planning/phases/03-page-metadata-pagination/03-RESEARCH.md b/.planning/phases/03-page-metadata-pagination/03-RESEARCH.md
new file mode 100644
index 0000000..be4737d
--- /dev/null
+++ b/.planning/phases/03-page-metadata-pagination/03-RESEARCH.md
@@ -0,0 +1,144 @@
+# Phase 3: 重构属性体系与复杂分页器 - Research
+
+**Researched:** 2026-05-22
+**Domain:** iOS EPUB native pagination / page metadata / CoreText layout semantics
+**Confidence:** HIGH
+
+
+## User Constraints
+
+No `CONTEXT.md` exists for this phase. Planning is based on ROADMAP requirements, current code, Phase 1 strategy, and Phase 2 implementation artifacts only.
+
+
+
+## Architectural Responsibility Map
+
+| Capability | Primary Tier | Secondary Tier | Rationale |
+|------------|-------------|----------------|-----------|
+| Page-level attributed string metadata | EPUBTextRendering | EPUBCore | Metadata must stay adjacent to chapter/page models so offsets, fragments, and attachments remain queryable. |
+| Stronger pagination semantics | EPUBTextRendering | CoreText | `ss_pageRanges(size:)` is the only current paginator; Phase 3 must replace or wrap it with richer layout decisions. |
+| Offset continuity for location/highlight/search | EPUBTextRendering | EPUBUI | `RDEPUBTextBook`, `RDEPUBTextContentView`, and `RDEPUBReadingSession` all assume stable chapter offsets. |
+| Reader state + navigation orchestration | EPUBCore | EPUBUI | `RDEPUBReadingSession` should keep owning navigation/page lookup; it should consume richer page metadata rather than invent a new UI model. |
+| Fixed / interactive EPUB rendering | EPUBCore | WebKit | Still out of scope; WebView paths remain unchanged. |
+
+
+
+
+## Summary
+
+Phase 2 solved the renderer input problem. The native reflowable path now has chapter-scoped preprocessing, explicit CSS precedence, and resolver-backed resource diagnostics. What remains weak is the page model and paginator. `RDEPUBTextPaginationSupport.ss_pageRanges(size:)` still slices the chapter with `CTFrameGetVisibleStringRange` and returns only bare `NSRange` values. `RDEPUBTextPage` then stores those ranges plus `pageStartOffset` / `pageEndOffset`, but no block metadata, no attachment semantics, no page-edge reasons, and no way to explain why a page boundary landed where it did.
+
+That limitation is now the main blocker for `REND-03` and `REND-04`. Search, selection, highlights, `RDEPUBReadingSession.pageIndex(for:)`, and `RDEPUBTextContentView` all depend on stable offsets. Phase 3 therefore cannot replace `RDEPUBTextBook` with a new UI-only model. It has to extend the existing `RDEPUBTextChapter` / `RDEPUBTextPage` contract with richer metadata while preserving chapter text continuity and global offsets.
+
+The safest implementation path is incremental:
+
+1. Define page metadata and attribute keys first.
+2. Introduce an internal layouter / layout-frame abstraction that can reason about blocks, attachments, and page edges while still returning offset-stable page slices.
+3. Validate the new paginator on real EPUBs with large blocks and images before touching Phase 4 reader reintegration.
+
+The repo already exposes the exact compatibility surface we need to preserve:
+
+- `RDEPUBTextContentView` derives relative highlight/search ranges from `pageStartOffset` and `pageEndOffset`.
+- `RDEPUBTextOffsetRangeInfo` serializes highlight ranges as absolute chapter offsets.
+- `RDEPUBTextSearchEngine` indexes chapter-level attributed strings and stores match ranges by chapter offset.
+- `RDEPUBReadingSession` still resolves navigation by href + progression and maps that back onto active pages.
+
+**Primary recommendation:** Build a richer internal pagination layer under `EPUBTextRendering` that emits extended `RDEPUBTextPage` metadata while keeping `pageStartOffset`, `pageEndOffset`, `fragmentOffsets`, and chapter text continuity intact. Do not move layout logic into `RDEPUBReaderController` or `RDEPUBTextContentView`.
+
+
+
+## Standard Stack
+
+### Core
+| Library | Version | Purpose | Why Standard |
+|---------|---------|---------|--------------|
+| Foundation | System | Metadata models, ranges, serialization, diagnostics | Existing page/highlight/search models are already Foundation-driven. |
+| CoreText | System | Low-level line/frame measurement | Needed to upgrade beyond pure visible-range slicing. |
+| UIKit | iOS 15+ SDK | Text page presentation and selection mapping | `RDEPUBTextContentView` still presents `NSAttributedString` pages and maps ranges back to offsets. |
+| DTCoreText | 1.6.28 | HTML-to-attributed-string import | Phase 3 builds on imported attributed content rather than replacing the typesetter. |
+
+### Supporting
+| Library | Version | Purpose | When to Use |
+|---------|---------|---------|-------------|
+| ZIPFoundation | 0.9.20 | EPUB archive access | Real-book validation still depends on sample EPUB extraction. |
+| DTFoundation | 1.7.19 | DTCoreText support | Attachment and HTML import remain rooted here. |
+
+### Alternatives Considered
+| Instead of | Could Use | Tradeoff |
+|------------|-----------|----------|
+| Extend `RDEPUBTextPage` / `RDEPUBTextChapter` | Create a brand-new page tree model | Would break current reader/highlight/search callers too early and enlarge Phase 4 scope. |
+| Introduce layouter/frame types under `EPUBTextRendering` | Keep growing `ss_pageRanges(size:)` as a single function | Hard to express page-edge reasons, block avoidance rules, and attachment metadata in one range-only helper. |
+| Preserve chapter offsets as the compatibility key | Shift to page-local only locations | Would break selection/highlight persistence and make Phase 4 much harder. |
+
+
+
+
+## Architecture Patterns
+
+### Pattern 1: Extend, don't replace, the book contract
+**What:** Add metadata to `RDEPUBTextPage` / `RDEPUBTextChapter` while preserving existing offset fields.
+**When to use:** When downstream readers already depend on `href + offsets + pages`.
+**Why:** Highlights, search matches, and navigation already serialize offsets and chapter hrefs.
+
+### Pattern 2: Separate layouter from page serialization
+**What:** Use internal layouter/layout-frame types to compute page boundaries, then convert those results into `RDEPUBTextPage`.
+**When to use:** When pagination logic is becoming more complex than plain visible-range slicing.
+**Why:** Keeps `RDEPUBTextBookBuilder` as the chapter/page assembly boundary without forcing it to own all layout heuristics inline.
+
+### Pattern 3: Preserve chapter-level continuity as the invariant
+**What:** Every new metadata structure should be traceable back to absolute chapter offsets.
+**When to use:** For block edges, attachments, fragment anchors, and page break reasons.
+**Why:** Current location/highlight/search behavior uses absolute chapter ranges; Phase 3 should enrich that graph, not replace it.
+
+### Anti-Patterns to Avoid
+- Rewriting `RDReaderView` or page presentation controls in the name of pagination.
+- Introducing metadata that cannot be mapped back to `pageStartOffset` / `pageEndOffset`.
+- Letting validation rely only on “looks okay” manual checks without capturing page-edge or attachment evidence.
+
+
+
+
+## Don't Hand-Roll
+
+| Problem | Don't Build | Use Instead | Why |
+|---------|-------------|-------------|-----|
+| Reader reintegration | A new reader shell | Existing `RDEPUBTextBook` / `RDEPUBReadingSession` contract | Phase 4 needs continuity, not a second UI model. |
+| Location persistence | Page-local ephemeral IDs only | Absolute chapter offsets + fragment compatibility | Current highlights and search results already use offset-based range info. |
+| Validation | Purely visual checks with no metadata evidence | Source assertions plus sample EPUB diagnostics | Complex pagination bugs need explainable metadata, not only screenshots. |
+
+
+
+
+## Common Pitfalls
+
+### Pitfall 1: Richer metadata that doesn't survive page serialization
+If metadata lives only in temporary layout structures and never lands on `RDEPUBTextPage`, Phase 4 cannot consume it.
+
+### Pitfall 2: Better page edges but broken offset continuity
+If page boundaries improve but `pageStartOffset` / `pageEndOffset` drift from chapter content, highlights and search navigation regress immediately.
+
+### Pitfall 3: Attachment-aware pagination without validation hooks
+Image/attachment rules are hard to trust unless the phase leaves behind enough metadata or demo diagnostics to confirm what happened on a given page boundary.
+
+
+
+
+## State of the Art (2024-2025)
+
+| Old Approach | Current Approach | Impact |
+|--------------|------------------|--------|
+| `CTFrameGetVisibleStringRange` only | Layout pipeline with page-edge semantics and attachment-aware metadata | Needed to express avoid-break rules and richer page reasoning. |
+| Range-only page model | Page model with semantic metadata and diagnostics | Needed for later reader reintegration and regression debugging. |
+| Paginator hidden inside a helper | Layouter/frame responsibilities separated from book assembly | Improves testability and makes pagination regressions explainable. |
+
+
+
+
+## Open Questions
+
+- Which page metadata belongs directly on `RDEPUBTextPage` versus a nested layout-metadata payload?
+- Should attachment/block metadata be encoded as custom `NSAttributedString.Key` values, separate page structs, or both?
+- How much of the new layouter/frame API should remain internal to `EPUBTextRendering` versus surfaced through `RDEPUBTextBookBuilder`?
+- Which sample EPUBs in `ReadViewDemo/ReadViewDemo/book/` best expose large-block and image boundary cases for repeatable validation?
+
+
diff --git a/.planning/phases/03-page-metadata-pagination/03-VALIDATION.md b/.planning/phases/03-page-metadata-pagination/03-VALIDATION.md
new file mode 100644
index 0000000..a7964c1
--- /dev/null
+++ b/.planning/phases/03-page-metadata-pagination/03-VALIDATION.md
@@ -0,0 +1,63 @@
+---
+phase: 3
+slug: page-metadata-pagination
+status: draft
+nyquist_compliant: true
+wave_0_complete: false
+created: 2026-05-22
+---
+
+# Phase 3 — Validation Strategy
+
+> Per-phase validation contract for page metadata and paginator-semantic work.
+
+---
+
+## Test Infrastructure
+
+| Property | Value |
+|----------|-------|
+| **Framework** | shell assertions + Xcode build smoke + demo EPUB diagnostics |
+| **Config file** | none — repo still has no dedicated XCTest target for this phase |
+| **Quick run command** | `test -f .planning/phases/03-page-metadata-pagination/03-RESEARCH.md && test -f .planning/phases/03-page-metadata-pagination/03-PATTERNS.md` |
+| **Full suite command** | `xcodebuild -workspace ReadViewDemo/ReadViewDemo.xcworkspace -scheme ReadViewDemo -destination 'platform=iOS Simulator,name=iPhone 17' build` |
+| **Estimated runtime** | ~60-90 seconds |
+
+## Sampling Rate
+
+- **After every task commit:** Run the task’s `rg` assertions against touched rendering/pagination files.
+- **After every wave:** Run the `ReadViewDemo` simulator build smoke.
+- **Before `$gsd-verify-work`:** Demo sample EPUB diagnostics must still pass, and Phase 3 plans/summaries must exist.
+- **Max feedback latency:** 90 seconds
+
+## Per-Task Verification Map
+
+| Task ID | Plan | Wave | Requirement | Secure Behavior | Test Type | Automated Command | Status |
+|---------|------|------|-------------|-----------------|-----------|-------------------|--------|
+| 03-01-01 | 01 | 1 | REND-03 | Page/chapter models expose richer metadata without dropping offset continuity | source | `rg -n "metadata|attachment|block|pageStartOffset|pageEndOffset|fragmentOffsets" Sources/RDReaderView/EPUBTextRendering Sources/RDReaderView/EPUBCore` | ⬜ pending |
+| 03-02-01 | 02 | 2 | REND-04 | Paginator no longer relies on pure visible-range slicing and introduces layouter/frame semantics | source | `rg -n "Layouter|LayoutFrame|pageBreak|avoid|attachment|CTTypesetter|CTFramesetter" Sources/RDReaderView/EPUBTextRendering` | ⬜ pending |
+| 03-03-01 | 03 | 3 | REND-03, REND-04 | Sample EPUBs and page diagnostics prove large blocks/images keep offset-compatible pagination | smoke/source | `rg -n "diagnostic|metadata|page break|attachment|image|block" Sources/RDReaderView ReadViewDemo/ReadViewDemo` | ⬜ pending |
+
+## Wave 0 Requirements
+
+- [ ] `ReadViewDemo/ReadViewDemo.xcworkspace` and `ReadViewDemo` scheme still build before close-out.
+- [ ] Validation continues to use `ReadViewDemo/ReadViewDemo/book/` as the regression corpus.
+- [ ] No plan may remove `pageStartOffset`, `pageEndOffset`, or fragment offset compatibility.
+
+## Manual-Only Verifications
+
+| Behavior | Requirement | Why Manual | Test Instructions |
+|----------|-------------|------------|-------------------|
+| Large blocks and images break at better boundaries | REND-04 | Visual edge quality still needs a human read-through | Open a sample EPUB with images and long paragraphs; confirm boundaries are improved versus old slicing. |
+| Highlight/search navigation still lands on the correct page after repagination | REND-03, REND-04 | Requires end-to-end interaction with existing reader flow | Use the demo reader to navigate to saved ranges or search matches after pagination changes. |
+
+## Validation Sign-Off
+
+- [ ] All tasks have automated verification commands
+- [ ] Sampling continuity: no 3 consecutive tasks without automated verify
+- [ ] Wave 0 covers offset-compatibility invariants
+- [ ] No watch-mode flags
+- [ ] Feedback latency < 90s
+- [ ] `nyquist_compliant: true` set in frontmatter
+
+**Approval:** pending
diff --git a/.planning/phases/03-page-metadata-pagination/03-VERIFICATION.md b/.planning/phases/03-page-metadata-pagination/03-VERIFICATION.md
new file mode 100644
index 0000000..e019d84
--- /dev/null
+++ b/.planning/phases/03-page-metadata-pagination/03-VERIFICATION.md
@@ -0,0 +1,31 @@
+# Phase 3 Verification
+
+## Commands
+
+- `pod install` in `ReadViewDemo/`
+- `build_sim` for scheme `ReadViewDemo`
+- `build_run_sim` for scheme `ReadViewDemo`
+- `rg -n "diagnostic|metadata|page break|attachment|image|block|pageStartOffset|pageEndOffset|RDEPUBTextOffsetRangeInfo|rangeInfo" Sources/RDReaderView ReadViewDemo/ReadViewDemo -S`
+- `test -d ReadViewDemo/ReadViewDemo/book`
+
+## Results
+
+- Build succeeded on iOS Simulator
+- App launch succeeded on simulator `iPhone 17`
+- Sample corpus directory exists: `ReadViewDemo/ReadViewDemo/book`
+- Source verification confirms:
+ - internal layouter/layout-frame types exist under `EPUBTextRendering`
+ - paginator now emits page-break semantics and attachment/block diagnostics
+ - `RDEPUBTextContentView` still uses `pageStartOffset` / `pageEndOffset`
+ - `RDEPUBTextOffsetRangeInfo` remains the absolute-offset persistence format
+
+## Runtime Evidence
+
+From the app runtime log:
+
+- `EPUB 资源验证:2/2 通过`
+- `分页诊断:宝山辽墓材料与释读 · 章节 10 · attachment 页 54 · semantic break 页 57 · reasons [attachmentBoundary:31, blockBoundary:26, frameLimit:17, chapterEnd:4] · page break: blockBoundary`
+
+## Residual Risk
+
+- The layouter heuristics are intentionally incremental, not a full WXRead-equivalent paginator. Phase 4 should validate the stronger page model against live reader navigation/highlight/search flows, and Phase 5 should broaden corpus coverage.
diff --git a/.planning/phases/04-reader-capabilities/04-01-PLAN.md b/.planning/phases/04-reader-capabilities/04-01-PLAN.md
new file mode 100644
index 0000000..740dd88
--- /dev/null
+++ b/.planning/phases/04-reader-capabilities/04-01-PLAN.md
@@ -0,0 +1,89 @@
+---
+phase: 04-reader-capabilities
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ - Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
+ - Sources/RDReaderView/RDURLReaderController.swift
+autonomous: true
+requirements:
+ - COMP-01
+user_setup: []
+must_haves:
+ truths:
+ - native text pages remain rendered through the existing `RDEPUBReaderController` + `RDReaderView` shell.
+ - no new page-turn container or parallel native reader shell is introduced.
+ - `RDEPUBTextBook` remains the native reflowable backend contract.
+ artifacts:
+ - .planning/phases/04-reader-capabilities/04-01-SUMMARY.md
+ key_links:
+ - `RDEPUBReaderController.paginatePublication`
+ - `RDEPUBReaderController.pageContentView`
+ - `RDEPUBReaderController.restoreReadingLocation`
+---
+
+
+把 Phase 3 的 native text backend 稳定接回现有 reader 主流程,让 `RDEPUBReaderController` 对 `RDEPUBTextBook` 路径的分页、展示、恢复行为更完整,但不修改 `RDReaderView`。
+
+Purpose: 让 reader 主流程明确把 native reflowable path 当成正式 backend,而不是零散的特殊分支。
+Output: reader-controller/native-text integration cleanup with clearer state handoff and no changes to the page-turn container.
+
+
+
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/04-reader-capabilities/04-RESEARCH.md
+@.planning/phases/04-reader-capabilities/04-PATTERNS.md
+@.planning/phases/03-page-metadata-pagination/03-RESEARCH.md
+@.planning/phases/03-page-metadata-pagination/03-VERIFICATION.md
+@Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+@Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
+@Sources/RDReaderView/RDURLReaderController.swift
+
+
+
+
+
+ Task 1: 收敛 native text backend 的分页完成与展示入口
+ Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift
+ 整理 `paginatePublication`、`applyTextBook`、`finishPagination`、`currentVisibleLocation` 和 `restoreReadingLocation` 的 native text 行为,让 `RDEPUBTextBook` 路径拥有清晰的完成/恢复入口,必要时补充最小状态桥接,但不要改 `RDReaderView` 的翻页接口,也不要引入新的 reader shell。
+
+ - `RDEPUBReaderController` remains the single entry point for native text and web reading
+ - text-book pagination completion and restore logic are explicit and internally coherent
+ - no code moves page-turn logic into `RDEPUBTextBookBuilder` or `RDEPUBTextContentView`
+
+ rg -n "paginatePublication|applyTextBook|finishPagination|restoreReadingLocation|currentVisibleLocation" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
+ native text pagination is reconnected as a first-class reader backend.
+
+
+
+ Task 2: 保持 page container 兼容并清理 native text 初始化入口
+ Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift, Sources/RDReaderView/RDURLReaderController.swift
+ Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift, Sources/RDReaderView/RDURLReaderController.swift
+ 确认并收敛 `pageContentView(readerView:pageNum:containerView:)`、TXT 入口和 native content view 配置路径,使 `RDReaderView` 继续只消费 UIView 页面内容而不用理解新的 page metadata。若需要暴露更多 page context,优先通过现有 content-view/controller 接口完成。
+
+ - `RDReaderView` does not gain native metadata-specific branching
+ - `RDEPUBTextContentView` remains the native page presenter
+ - TXT/native-text initialization path remains compatible with the same reader shell
+
+ rg -n "pageContentView|RDEPUBTextContentView|RDURLReaderController|textBook" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/RDURLReaderController.swift Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
+ reader presentation stays container-compatible while native text becomes a stable backend.
+
+
+
+
+
+Before declaring plan complete:
+- [ ] `rg -n "paginatePublication|applyTextBook|finishPagination|restoreReadingLocation|currentVisibleLocation" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`
+- [ ] `rg -n "pageContentView|RDEPUBTextContentView|RDURLReaderController|textBook" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/RDURLReaderController.swift Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift`
+
+
+
diff --git a/.planning/phases/04-reader-capabilities/04-01-SUMMARY.md b/.planning/phases/04-reader-capabilities/04-01-SUMMARY.md
new file mode 100644
index 0000000..6ac6270
--- /dev/null
+++ b/.planning/phases/04-reader-capabilities/04-01-SUMMARY.md
@@ -0,0 +1,16 @@
+# 04-01 Summary
+
+## Outcome
+
+Wave 1 reconnected the native text backend as a first-class reader path inside the existing shell.
+
+- `RDEPUBReaderController` now builds a real native snapshot from `RDEPUBTextBook` instead of leaving `activePages` / `activeChapters` empty
+- Native restore and current-location resolution now go through shared controller helpers instead of scattered text-book special cases
+- Native page turns update `RDEPUBReadingSession` with page/spine/chapter context, so reader state stays coherent with the visible page
+- `RDReaderView` remained unchanged; it still only consumes content views and page counts
+
+## Compatibility
+
+- No new reader shell or page-turn container was introduced
+- TXT and EPUB native text flows still use `RDEPUBReaderController` and `RDEPUBTextContentView`
+- `RDEPUBTextBook` remains the backend contract for native reflowable pages
diff --git a/.planning/phases/04-reader-capabilities/04-02-PLAN.md b/.planning/phases/04-reader-capabilities/04-02-PLAN.md
new file mode 100644
index 0000000..8c058da
--- /dev/null
+++ b/.planning/phases/04-reader-capabilities/04-02-PLAN.md
@@ -0,0 +1,90 @@
+---
+phase: 04-reader-capabilities
+plan: 02
+type: execute
+wave: 2
+depends_on:
+ - "04-01"
+files_modified:
+ - Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
+ - Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift
+ - Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift
+ - Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift
+autonomous: true
+requirements:
+ - COMP-03
+user_setup: []
+must_haves:
+ truths:
+ - native text highlight/search behavior remains grounded in absolute chapter offsets.
+ - location mapping and page-number lookup stay coherent after the reader integration cleanup.
+ - no page-local-only persistence identifier replaces `RDEPUBTextOffsetRangeInfo`.
+ artifacts:
+ - .planning/phases/04-reader-capabilities/04-02-SUMMARY.md
+ key_links:
+ - `RDEPUBReaderController.normalizedTextSelection`
+ - `RDEPUBReaderController.search`
+ - `RDEPUBReadingSession.pageIndex(for:)`
+---
+
+
+修复并验证 native text reader 下的位置映射、高亮、选区、搜索和 TOC/跳转兼容能力,确保 Phase 3 的 richer paginator 不会破坏这些用户能力。
+
+Purpose: 把 offset-compatible 的底层契约真正接回 reader 行为链路。
+Output: source fixes and verification around native location lookup, selection normalization, search navigation, and persisted highlights.
+
+
+
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/04-reader-capabilities/04-RESEARCH.md
+@.planning/phases/04-reader-capabilities/04-PATTERNS.md
+@Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
+@Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift
+@Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift
+@Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift
+
+
+
+
+
+ Task 1: 收敛 native text 的 location / selection / highlight 兼容语义
+ Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift
+ Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ 验证并修复 native text selection/highlight 链路,重点围绕 `normalizedTextSelection`、`RDEPUBTextOffsetRangeInfo`、`pageStartOffset` / `pageEndOffset` overlap 计算和 reader persistence 语义展开。若要补充 guard 或 normalization,必须继续以 chapter href + absolute offsets 为核心,而不是引入 page-local-only id。
+
+ - `RDEPUBTextOffsetRangeInfo` remains the persisted native text range format
+ - native selection/highlight restore still maps through absolute chapter offsets
+ - `RDEPUBTextContentView` still derives visible ranges from page offsets
+
+ rg -n "RDEPUBTextOffsetRangeInfo|normalizedTextSelection|pageStartOffset|pageEndOffset|rangeInfo" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift Sources/RDReaderView/EPUBCore
+ native text highlight and selection persistence remain offset-compatible.
+
+
+
+ Task 2: 修复并验证 native text 搜索 / TOC / 跳转页号解析
+ Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift
+ Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift
+ 核对 native text path 的 `pageNumber(for:)`、`search`、`navigateToCurrentSearchMatch`、TOC 页号解析与内部链接跳转,确保 richer page model 下这些 reader feature 仍能落到正确页。需要时可以增强 lookup/normalization,但不要把 Web 路径逻辑硬搬到 native path。
+
+ - native text search still produces usable page navigation
+ - TOC/page-number resolution stays coherent for `RDEPUBTextBook`
+ - reader state and page lookup are not duplicated into a second custom navigation stack
+
+ rg -n "search\\(|navigateToCurrentSearchMatch|pageNumber\\(for: searchMatch\\)|flattenedTableOfContentsItems|pageIndex\\(for:" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift
+ native text search, TOC, and jumps remain believable after reintegration.
+
+
+
+
+
+Before declaring plan complete:
+- [ ] `rg -n "RDEPUBTextOffsetRangeInfo|normalizedTextSelection|pageStartOffset|pageEndOffset|rangeInfo" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift Sources/RDReaderView/EPUBCore`
+- [ ] `rg -n "search\\(|navigateToCurrentSearchMatch|pageNumber\\(for: searchMatch\\)|flattenedTableOfContentsItems|pageIndex\\(for:" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.swift`
+
+
+
diff --git a/.planning/phases/04-reader-capabilities/04-02-SUMMARY.md b/.planning/phases/04-reader-capabilities/04-02-SUMMARY.md
new file mode 100644
index 0000000..57271a2
--- /dev/null
+++ b/.planning/phases/04-reader-capabilities/04-02-SUMMARY.md
@@ -0,0 +1,15 @@
+# 04-02 Summary
+
+## Outcome
+
+Wave 2 tightened native location, highlight, selection, search, and TOC compatibility around the Phase 3 offset contract.
+
+- `normalizedTextSelection` now preserves exclusive end semantics more carefully when rebuilding progressions from `RDEPUBTextOffsetRangeInfo`
+- Native highlight filtering now compares normalized hrefs, reducing mismatch risk after resolver normalization
+- Search page lookup now prefers exact `rangeLocation` offset matching before falling back to progression-based page resolution
+
+## Compatibility
+
+- `RDEPUBTextOffsetRangeInfo` remains the native persisted range format
+- `RDEPUBTextContentView` still derives visible overlap from `pageStartOffset` / `pageEndOffset`
+- No page-local-only identifier replaced the absolute chapter offset contract
diff --git a/.planning/phases/04-reader-capabilities/04-03-PLAN.md b/.planning/phases/04-reader-capabilities/04-03-PLAN.md
new file mode 100644
index 0000000..155b5d5
--- /dev/null
+++ b/.planning/phases/04-reader-capabilities/04-03-PLAN.md
@@ -0,0 +1,90 @@
+---
+phase: 04-reader-capabilities
+plan: 03
+type: execute
+wave: 3
+depends_on:
+ - "04-02"
+files_modified:
+ - Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
+ - Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift
+ - Sources/RDReaderView/RDURLReaderController.swift
+ - ReadViewDemo/ReadViewDemo/ViewController.swift
+autonomous: true
+requirements:
+ - COMP-04
+user_setup: []
+must_haves:
+ truths:
+ - repagination after font, line-height, theme, or viewport changes restores semantic reading state.
+ - the demo app remains the primary runtime verification entry point.
+ - `RDReaderView` does not need modification to validate these flows.
+ artifacts:
+ - .planning/phases/04-reader-capabilities/04-03-SUMMARY.md
+ key_links:
+ - `RDEPUBReaderController.configuration.didSet`
+ - `RDEPUBReaderController.handleViewportChangeIfNeeded`
+ - `RDEPUBReaderController.rebuildExternalTextBook`
+---
+
+
+验证并收敛字体、行高、主题和 viewport 变化导致的重新分页与状态恢复,让 reader 在 Phase 4 完成后可以稳定承接 repagination。
+
+Purpose: 证明新 native backend 在真实 reader 交互下能够重新分页而不丢失位置、搜索和高亮上下文。
+Output: re-pagination/state-restore fixes, plus demo-visible validation hooks or diagnostics where needed.
+
+
+
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/04-reader-capabilities/04-RESEARCH.md
+@.planning/phases/04-reader-capabilities/04-PATTERNS.md
+@.planning/phases/03-page-metadata-pagination/03-VERIFICATION.md
+@Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
+@Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift
+@Sources/RDReaderView/RDURLReaderController.swift
+@ReadViewDemo/ReadViewDemo/ViewController.swift
+
+
+
+
+
+ Task 1: 收敛 repagination 触发器与状态恢复逻辑
+ Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift, Sources/RDReaderView/RDURLReaderController.swift
+ Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift, Sources/RDReaderView/RDURLReaderController.swift
+ 围绕 `configuration.didSet`、`requiresRepagination`、`refreshVisibleContentPreservingLocation`、`rebuildExternalTextBook`、`handleViewportChangeIfNeeded` 和 TXT/native 入口收敛 repagination 行为,使 reader 在设置变化与 viewport 变化后恢复到语义位置,而不是错误页或空状态。
+
+ - font size / line height changes trigger repagination through semantic location restore
+ - theme changes refresh visible content without dropping current reader state
+ - viewport changes do not require `RDReaderView` changes to preserve location
+
+ rg -n "requiresRepagination|refreshVisibleContentPreservingLocation|rebuildExternalTextBook|handleViewportChangeIfNeeded|configuration" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift Sources/RDReaderView/RDURLReaderController.swift
+ repagination triggers are stable and state-preserving.
+
+
+
+ Task 2: 在 demo 中验证 reader-level repagination 与状态恢复
+ ReadViewDemo/ReadViewDemo/ViewController.swift, Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
+ ReadViewDemo/ReadViewDemo/ViewController.swift, Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, ReadViewDemo/ReadViewDemo/book/
+ 用现有 demo 入口补充最小但可观察的 reader-level 验证路径或日志,证明 Phase 4 关注的 repagination / location restore / search-highlight continuity 可以被检查。不要新建并行 demo shell。
+
+ - demo remains the validation entry point
+ - runtime checks can confirm at least part of the repagination/state-restore behavior
+ - sample corpus under `ReadViewDemo/ReadViewDemo/book/` remains the verification source
+
+ test -d ReadViewDemo/ReadViewDemo/book && rg -n "restore|repagination|search|highlight|location|theme|font" ReadViewDemo/ReadViewDemo Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
+ Phase 4 finishes with a real runtime path to prove state-preserving repagination.
+
+
+
+
+
+Before declaring plan complete:
+- [ ] `rg -n "requiresRepagination|refreshVisibleContentPreservingLocation|rebuildExternalTextBook|handleViewportChangeIfNeeded|configuration" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBUI/RDEPUBReaderConfiguration.swift Sources/RDReaderView/RDURLReaderController.swift`
+- [ ] `test -d ReadViewDemo/ReadViewDemo/book`
+- [ ] `rg -n "restore|repagination|search|highlight|location|theme|font" ReadViewDemo/ReadViewDemo Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`
+
+
+
diff --git a/.planning/phases/04-reader-capabilities/04-03-SUMMARY.md b/.planning/phases/04-reader-capabilities/04-03-SUMMARY.md
new file mode 100644
index 0000000..7d62dfc
--- /dev/null
+++ b/.planning/phases/04-reader-capabilities/04-03-SUMMARY.md
@@ -0,0 +1,19 @@
+# 04-03 Summary
+
+## Outcome
+
+Wave 3 validated repagination and semantic location restore with real sample books.
+
+- The demo now reports a restore diagnostic alongside the pagination diagnostic
+- Validation rebuilds native text books with a larger font/line spacing and a theme-only style change, then checks whether the same semantic location still resolves correctly
+- Runtime evidence confirmed stable href restoration and theme-stable page mapping on the sampled reflowable EPUB
+
+## Demo Evidence
+
+Runtime log from the simulator:
+
+- `EPUB 资源验证:2/2 通过`
+- `分页诊断:宝山辽墓材料与释读 · 章节 10 · attachment 页 54 · semantic break 页 57 · reasons [attachmentBoundary:31, blockBoundary:26, frameLimit:17, chapterEnd:4] · page break: blockBoundary`
+- `恢复诊断:宝山辽墓材料与释读 · base 39 · font-shift 44 · theme-stable yes · href-stable yes · progression-delta 0.004`
+
+This shows the native reader backend can repaginate to a different page number under font changes while preserving the same semantic reading location.
diff --git a/.planning/phases/04-reader-capabilities/04-PATTERNS.md b/.planning/phases/04-reader-capabilities/04-PATTERNS.md
new file mode 100644
index 0000000..5ef9c78
--- /dev/null
+++ b/.planning/phases/04-reader-capabilities/04-PATTERNS.md
@@ -0,0 +1,29 @@
+# Phase 4: 接回现有 reader 能力链路 - Pattern Map
+
+## Goal
+
+为 Phase 4 提供“先接哪里、怎么验证没把 reader 行为搞坏”的最短路径。重点不是继续改 paginator,而是把已经完成的 native text book/page model 稳定接回 reader 能力链路。
+
+## Planned Outputs
+
+| Planned file | Role | Primary evidence | Why this is the right analog |
+|--------------|------|------------------|------------------------------|
+| `04-01-PLAN.md` | native text backend 接回 reader 主流程 | `RDEPUBReaderController`, `RDEPUBTextBookBuilder`, `RDEPUBTextContentView`, `RDURLReaderController` | 这些文件定义当前 native text 打开、分页、展示和 page container 接口。 |
+| `04-02-PLAN.md` | 位置映射 / 高亮 / 搜索兼容收敛 | `RDEPUBReadingSession`, `RDEPUBReaderController`, `RDEPUBTextSearchEngine`, `RDEPUBReadingModels` | 这些文件共同定义 reader state、search matches、selection normalization 与绝对 offset 持久化语义。 |
+| `04-03-PLAN.md` | 主题 / 字号 / 行高 / viewport 变化后的重新分页与状态恢复验证 | `RDEPUBReaderController`, `RDEPUBReaderConfiguration`, `RDURLReaderController`, `ReadViewDemo` | 当前所有重新分页触发器和 demo 验证入口都在这里。 |
+
+## Code Evidence Map
+
+| Concern | Closest source of truth | Evidence to extract |
+|---------|-------------------------|---------------------|
+| native text page flow接入点 | `RDEPUBReaderController.paginatePublication`, `applyTextBook`, `pageContentView` | 现有 controller 已有 native/web 双路径,但需要收敛行为。 |
+| 恢复位置与可见位置 | `restoreReadingLocation`, `currentVisibleLocation`, `finishPagination` | 这里决定 repagination 后回到哪里。 |
+| 绝对 offset 高亮/选区兼容 | `RDEPUBTextContentView`, `normalizedTextSelection`, `RDEPUBTextOffsetRangeInfo` | 这些是 Phase 4 不能破坏的硬约束。 |
+| 搜索跳转与页号解析 | `search`, `navigateToCurrentSearchMatch`, `pageNumber(for:)`, `RDEPUBTextSearchEngine` | 需要保证 native text path 仍能定位正确页。 |
+| 设置变更触发重排 | `configuration.didSet`, `requiresRepagination`, `refreshVisibleContentPreservingLocation`, `handleViewportChangeIfNeeded` | 字号/行高/主题/旋转都是 Phase 4 的状态恢复验证入口。 |
+
+## Writing Guidance
+
+- 计划必须把“主流程接回、兼容能力收敛、重排恢复验证”拆成三个独立 plan,不要把所有 reader 能力挤进一个任务。
+- 任何修复都要优先围绕 `RDEPUBReaderController` 和 `RDEPUBReadingSession` 的现有责任边界展开,不要借机重写 `RDReaderView`。
+- 验证步骤要明确要求真实的 reader 行为检查,而不是只做 build pass。
diff --git a/.planning/phases/04-reader-capabilities/04-RESEARCH.md b/.planning/phases/04-reader-capabilities/04-RESEARCH.md
new file mode 100644
index 0000000..11c4afc
--- /dev/null
+++ b/.planning/phases/04-reader-capabilities/04-RESEARCH.md
@@ -0,0 +1,123 @@
+# Phase 4: 接回现有 reader 能力链路 - Research
+
+**Researched:** 2026-05-22
+**Domain:** iOS EPUB reader reintegration / location mapping / highlight-search-state continuity
+**Confidence:** HIGH
+
+
+## User Constraints
+
+No `CONTEXT.md` exists for this phase. Planning is based on ROADMAP requirements, current code, and completed artifacts from Phases 1-3 only.
+
+
+
+## Architectural Responsibility Map
+
+| Capability | Primary Tier | Secondary Tier | Rationale |
+|------------|-------------|----------------|-----------|
+| Reflowable native pagination output | EPUBTextRendering | EPUBCore | Phase 3 already moved richer page semantics into `RDEPUBTextBook` / `RDEPUBTextPage`. |
+| Reader orchestration and restore logic | EPUBUI | EPUBCore | `RDEPUBReaderController` owns `paginatePublication`, `finishPagination`, restore, search navigation, selection, and configuration updates. |
+| Page lookup and navigator state | EPUBCore | EPUBUI | `RDEPUBReadingSession` remains the non-UI state owner for fixed/web flows and should keep that role rather than pushing state into `RDReaderView`. |
+| Highlight/search persistence semantics | EPUBCore | EPUBUI | `RDEPUBTextOffsetRangeInfo` and normalized locations are the durable compatibility contract. |
+| Page-turn UI and display modes | RDReaderView | EPUBUI | Explicitly out of scope to change; Phase 4 must adapt to existing `RDReaderView` APIs. |
+
+
+
+
+## Summary
+
+Phase 3 strengthened the native reflowable engine but did not yet make that richer page model a first-class citizen across the whole reader capability chain. The main Phase 4 work is therefore not “more pagination”; it is reconnecting the existing reader shell to the upgraded `RDEPUBTextBook` path without changing `RDReaderView` or introducing a second native reader model.
+
+The current code already shows the exact reintegration seams:
+
+- `RDEPUBReaderController.paginatePublication` branches between native `RDEPUBTextBookBuilder`, fixed-layout snapshots, and `RDEPUBPaginator` WebKit measurement.
+- `applyTextBook`, `finishPagination`, `restoreReadingLocation`, and `currentVisibleLocation` already contain a parallel text-book path that bypasses `RDEPUBReadingSession.pageIndex(for:)`.
+- `pageContentView(readerView:pageNum:containerView:)` already swaps between `RDEPUBTextContentView` and `RDEPUBWebContentView`.
+- Search uses `RDEPUBTextSearchEngine` when `textBook` exists and `RDEPUBHTMLSearchEngine` otherwise.
+- Selection/highlight persistence for native text still depends on `RDEPUBTextOffsetRangeInfo` absolute offsets and chapter href normalization.
+
+That means Phase 4 should stabilize and tighten an already-existing split path rather than invent a new architecture. The right outcome is:
+
+1. Reflowable native pages remain rendered through `RDEPUBTextBook`.
+2. `RDEPUBReaderController` keeps one coherent restore/navigation/search/highlight API no matter whether the source page is native text or Web content.
+3. Repagination triggered by font size, line height, viewport changes, or theme changes preserves reader state instead of resetting to page 1 or dropping search/highlight context.
+
+The riskiest compatibility areas are also already visible:
+
+- `RDEPUBReaderController.restoreReadingLocation` and `currentVisibleLocation` use separate logic for text-book vs snapshot pages.
+- `normalizedTextSelection` recomputes progression from absolute offset payloads; regressions here would silently break persisted highlights.
+- `refreshVisibleContentPreservingLocation` and `rebuildExternalTextBook` can preserve location only if the text-book lookup contract remains stable after repagination.
+- `flattenedTableOfContentsItems` and `pageNumber(for:)` need page-number continuity for TOC and search to stay believable.
+
+**Primary recommendation:** Treat Phase 4 as a reader-state consolidation phase. Strengthen `RDEPUBReaderController` and adjacent state surfaces so the Phase 3 native pagination model behaves like a first-class reader backend while preserving the existing `RDReaderView` page-turn container and the fixed / interactive WebKit paths.
+
+
+
+## Code Evidence
+
+| Concern | Source | Why it matters |
+|--------|--------|----------------|
+| Native-vs-web pagination entry split | `RDEPUBReaderController.paginatePublication` | Defines Phase 4’s main re-integration entry point. |
+| Native restore and current-location logic | `RDEPUBReaderController.restoreReadingLocation`, `currentVisibleLocation` | Text-book navigation already bypasses `RDEPUBReadingSession`; this must stay coherent. |
+| Reader state owner for non-native flows | `RDEPUBReadingSession` | Existing navigation/session state should remain centralized instead of leaking into UI code. |
+| Native selection/highlight normalization | `RDEPUBReaderController.normalizedTextSelection`, `RDEPUBTextContentView`, `RDEPUBReadingModels` | Absolute offsets must survive reader interactions and persistence. |
+| Search backend switching | `RDEPUBReaderController.resolvedSearchMatches`, `RDEPUBTextSearchEngine` | Search behavior must remain uniform from the user’s perspective across render backends. |
+| Repagination triggers | `RDEPUBReaderController.configuration.didSet`, `handleViewportChangeIfNeeded`, `rebuildExternalTextBook`, `repaginatePreservingCurrentLocation` | These are the operational triggers most likely to break restore/state continuity. |
+
+
+
+
+## Architecture Patterns
+
+### Pattern 1: Consolidate reader behavior at the controller boundary
+**What:** Keep branching localized in `RDEPUBReaderController`, but make navigation, restore, search, and persistence semantics consistent across text-book and web paths.
+**Why:** The UI entry point already exists there; moving branching elsewhere would enlarge scope.
+
+### Pattern 2: Keep offsets as the native compatibility key
+**What:** Native text selection, search, and highlight flows should continue to resolve through chapter href + absolute offset payloads.
+**Why:** Phase 3 deliberately preserved this invariant to make Phase 4 possible.
+
+### Pattern 3: Repaginate by restoring location, not page number
+**What:** Font/theme/viewport changes should always preserve a semantic location and rebuild around it.
+**Why:** Page numbers are not stable under re-pagination; href + progression + offset-compatible selection are.
+
+### Anti-Patterns to Avoid
+- Refactoring `RDReaderView` to understand native page metadata directly.
+- Creating a second navigation state machine just for the native text path.
+- Switching highlights/search to page-local identifiers that cannot survive repagination.
+
+
+
+
+## Don't Hand-Roll
+
+| Problem | Don't Build | Use Instead | Why |
+|---------|-------------|-------------|-----|
+| Native reader shell | A new dedicated native-text controller | Existing `RDEPUBReaderController` split path | Scope stays contained and existing app entry points remain valid. |
+| Restore state | Page-number-only recovery | `RDEPUBLocation` + native page lookup + offset contract | Page numbers drift after repagination. |
+| Search compatibility | Separate search UI semantics for text pages | Existing `RDEPUBSearchState` and `RDEPUBSearchMatch` flow | User-facing search behavior should stay unified. |
+
+
+
+
+## Common Pitfalls
+
+### Pitfall 1: Letting native and web reader paths diverge in user-visible behavior
+If search, selection, or TOC navigation behaves differently based on backend, the reader becomes unpredictable.
+
+### Pitfall 2: Preserving page numbers instead of semantic locations during repagination
+This causes subtle wrong-page restores after font, theme, or viewport changes.
+
+### Pitfall 3: Refreshing visible content without reapplying current search/highlight state
+Native text pages are recreated from attributed substrings, so state must be re-derived every time.
+
+
+
+
+## Open Questions
+
+- Should any of the new Phase 3 page metadata be surfaced to reader delegates in Phase 4, or remain internal until Phase 5 diagnostics?
+- Is `RDEPUBReadingSession` worth extending to cover native `RDEPUBTextBook` page lookup as well, or is the current controller-local split still the smallest safe change?
+- Which search/highlight and TOC navigation paths are most important to validate interactively in the demo during execution?
+
+
diff --git a/.planning/phases/04-reader-capabilities/04-VALIDATION.md b/.planning/phases/04-reader-capabilities/04-VALIDATION.md
new file mode 100644
index 0000000..4ab3b44
--- /dev/null
+++ b/.planning/phases/04-reader-capabilities/04-VALIDATION.md
@@ -0,0 +1,29 @@
+# Phase 4: 接回现有 reader 能力链路 - Validation
+
+## Required Checks
+
+- `RDEPUBReaderController` 仍是 native text / web reader 的统一入口
+- `RDReaderView` 不需要新增 native page metadata 感知逻辑
+- native text 的高亮、搜索、TOC、恢复位置仍通过 `RDEPUBLocation` 与绝对 offset 兼容语义工作
+- 字号、行高、主题、viewport 变化后的 repagination 不会把阅读状态重置为错误位置
+
+## Verification Anchors
+
+### Source-level
+- `rg -n "paginatePublication|applyTextBook|restoreReadingLocation|currentVisibleLocation|finishPagination|pageContentView" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`
+- `rg -n "RDEPUBTextOffsetRangeInfo|normalizedTextSelection|search\\(|pageNumber\\(for: searchMatch\\)" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBCore Sources/RDReaderView/EPUBTextRendering`
+- `rg -n "requiresRepagination|refreshVisibleContentPreservingLocation|handleViewportChangeIfNeeded|rebuildExternalTextBook" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/RDURLReaderController.swift`
+
+### Runtime / demo
+- open a reflowable EPUB in `ReadViewDemo`
+- navigate via TOC and ensure the target page remains correct on the native text path
+- create or load a highlight, then verify it still paints after page refresh / repagination
+- run a search and verify current match navigation lands on the right page
+- change font size, line height, and theme; confirm the reader restores to the same semantic location
+
+## Completion Standard
+
+Phase 4 planning is only good enough if each executable plan names both:
+
+1. the code seam it will modify
+2. the reader behavior it must preserve or prove
diff --git a/.planning/phases/04-reader-capabilities/04-VERIFICATION.md b/.planning/phases/04-reader-capabilities/04-VERIFICATION.md
new file mode 100644
index 0000000..3ac46c7
--- /dev/null
+++ b/.planning/phases/04-reader-capabilities/04-VERIFICATION.md
@@ -0,0 +1,32 @@
+# Phase 4 Verification
+
+## Commands
+
+- `build_sim` for scheme `ReadViewDemo`
+- `build_run_sim` for scheme `ReadViewDemo`
+- `test -d ReadViewDemo/ReadViewDemo/book`
+- `rg -n "paginatePublication|applyTextBook|finishPagination|restoreReadingLocation|currentVisibleLocation|RDEPUBTextOffsetRangeInfo|normalizedTextSelection|pageStartOffset|pageEndOffset|search\\(|navigateToCurrentSearchMatch|pageNumber\\(for: searchMatch\\)|flattenedTableOfContentsItems|pageIndex\\(for:|requiresRepagination|refreshVisibleContentPreservingLocation|rebuildExternalTextBook|handleViewportChangeIfNeeded|restore|repagination|highlight|location|theme|font" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift Sources/RDReaderView/EPUBCore Sources/RDReaderView/RDURLReaderController.swift ReadViewDemo/ReadViewDemo -S`
+
+## Results
+
+- Simulator build succeeded
+- Simulator launch succeeded
+- Sample corpus directory exists
+- Source verification confirms:
+ - native text pagination now feeds a real reader snapshot and location helpers
+ - native selection/highlight compatibility still depends on absolute chapter offsets
+ - native search page lookup now uses exact offset evidence when available
+ - repagination and restore hooks remain centered in `RDEPUBReaderController`, not `RDReaderView`
+
+## Runtime Evidence
+
+From the app runtime log:
+
+- `EPUB 资源验证:2/2 通过`
+- `分页诊断:宝山辽墓材料与释读 · 章节 10 · attachment 页 54 · semantic break 页 57 · reasons [attachmentBoundary:31, blockBoundary:26, frameLimit:17, chapterEnd:4] · page break: blockBoundary`
+- `恢复诊断:宝山辽墓材料与释读 · base 39 · font-shift 44 · theme-stable yes · href-stable yes · progression-delta 0.004`
+
+## Residual Risk
+
+- The restore validation is still corpus-level and not a full interactive UI automation pass through TOC/highlight/search actions
+- The native and web paths still branch inside `RDEPUBReaderController`; Phase 5 should focus on broader regression and stability coverage rather than further architectural expansion
diff --git a/.planning/phases/05-regression-stability/05-01-PLAN.md b/.planning/phases/05-regression-stability/05-01-PLAN.md
new file mode 100644
index 0000000..79338e0
--- /dev/null
+++ b/.planning/phases/05-regression-stability/05-01-PLAN.md
@@ -0,0 +1,84 @@
+---
+phase: 05-regression-stability
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - ReadViewDemo/ReadViewDemo/ViewController.swift
+ - .planning/phases/05-regression-stability/05-VALIDATION.md
+ - .planning/phases/05-regression-stability/05-RESEARCH.md
+autonomous: true
+requirements:
+ - STAB-01
+user_setup: []
+must_haves:
+ truths:
+ - Phase 5 needs an explicit sample matrix, not only ad hoc simulator runs.
+ - demo/runtime diagnostics are the primary validation entry point.
+ - the matrix must account for native reflowable, fixed/interactive, and TXT-compatible paths.
+ artifacts:
+ - .planning/phases/05-regression-stability/05-01-SUMMARY.md
+ key_links:
+ - `ReadViewDemo/ReadViewDemo/book/`
+ - `ReadViewDemo/ReadViewDemo/ViewController.swift`
+---
+
+
+构建样本书验证矩阵、诊断输出和复现清单,让 Phase 5 的回归验证有明确覆盖面和可重复证据。
+
+Purpose: 先把“验证什么、用什么样本、看什么证据”固定下来,再进入稳定性收敛。
+Output: documented sample matrix plus demo-visible or log-visible validation summary covering the required book categories.
+
+
+
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/05-regression-stability/05-RESEARCH.md
+@.planning/phases/05-regression-stability/05-PATTERNS.md
+@.planning/phases/04-reader-capabilities/04-VERIFICATION.md
+@ReadViewDemo/ReadViewDemo/ViewController.swift
+@ReadViewDemo/ReadViewDemo/book/
+
+
+
+
+
+ Task 1: 定义样本矩阵与风险映射
+ .planning/phases/05-regression-stability/05-RESEARCH.md, .planning/phases/05-regression-stability/05-VALIDATION.md, ReadViewDemo/ReadViewDemo/ViewController.swift
+ .planning/phases/05-regression-stability/05-RESEARCH.md, ReadViewDemo/ReadViewDemo/ViewController.swift, ReadViewDemo/ReadViewDemo/book/
+ 明确每本样本书对应的验证类别与风险映射,并在需要时把这份矩阵体现在 demo 诊断输出或 phase 文档中。矩阵至少覆盖:小说/长文本 reflowable、复杂块元素/图片 reflowable、fixed 或 interactive WebKit 路径,以及 TXT 路径(如当前缺样本,则需在执行期明确补充策略)。
+
+ - sample corpus is mapped to explicit regression categories
+ - Phase 5 artifacts state what evidence each sample is expected to provide
+ - demo remains the primary visible validation entry point
+
+ find ReadViewDemo/ReadViewDemo/book -maxdepth 1 -type f | sort && rg -n "validation|diagnostic|sample|matrix|restore|fixed|reflowable|txt" ReadViewDemo/ReadViewDemo .planning/phases/05-regression-stability -S
+ Phase 5 has an explicit validation matrix instead of an implicit sample list.
+
+
+
+ Task 2: 增强可重复诊断输出与复现清单
+ ReadViewDemo/ReadViewDemo/ViewController.swift, .planning/phases/05-regression-stability/05-VALIDATION.md
+ ReadViewDemo/ReadViewDemo/ViewController.swift, .planning/phases/04-reader-capabilities/04-VERIFICATION.md
+ 在现有 demo summary 基础上补充最小但可重复的诊断输出或验证说明,使后续 Phase 5 回归结果能被再次运行并比对。不要引入新的测试壳;应复用当前 demo 和已有日志体系。
+
+ - runtime validation evidence is clearer than a generic success/failure line
+ - there is a written checklist or summary path for rerunning the same validations
+ - diagnostics remain lightweight and tied to existing sample books
+
+ rg -n "validation|diagnostic|restore|pagination|search|highlight|fixed|resource" ReadViewDemo/ReadViewDemo/ViewController.swift .planning/phases/05-regression-stability/05-VALIDATION.md
+ Phase 5 starts with a repeatable evidence-producing validation layer.
+
+
+
+
+
+Before declaring plan complete:
+- [ ] `find ReadViewDemo/ReadViewDemo/book -maxdepth 1 -type f | sort`
+- [ ] `rg -n "validation|diagnostic|sample|matrix|restore|fixed|reflowable|txt" ReadViewDemo/ReadViewDemo .planning/phases/05-regression-stability -S`
+
+
+
diff --git a/.planning/phases/05-regression-stability/05-01-SUMMARY.md b/.planning/phases/05-regression-stability/05-01-SUMMARY.md
new file mode 100644
index 0000000..2976a5f
--- /dev/null
+++ b/.planning/phases/05-regression-stability/05-01-SUMMARY.md
@@ -0,0 +1,21 @@
+# 05-01 Summary
+
+## Outcome
+
+Wave 1 turned the sample corpus into an explicit regression matrix and made the demo startup log the primary repeatable proof surface.
+
+- `ViewController` now validates every local `.epub` and `.txt` sample instead of only native reflowable EPUBs
+- startup summary now groups books into `TXT/小说`、`复杂图文`、`Fixed/互动`、`TXT` categories
+- a built-in TXT sample, `ReadViewDemo/ReadViewDemo/book/回归验证样本.txt`, now keeps the TXT path inside the repo instead of relying on an external file
+
+## Evidence
+
+Runtime log after simulator launch:
+
+- `样本验证:5/5 通过`
+- `矩阵[复杂图文] 2/2 · 宝山辽墓材料与释读(textReflowable)、张学良传(textReflowable)`
+- `矩阵[Fixed/互动] 2/2 · 《凡人修仙传》精校版全本(webInteractive)、爱忘事的熊爷爷(webFixedLayout)`
+- `矩阵[TXT] 1/1 · 回归验证样本(txt)`
+
+The demo remains the only validation surface; no extra test shell was introduced.
+
diff --git a/.planning/phases/05-regression-stability/05-02-PLAN.md b/.planning/phases/05-regression-stability/05-02-PLAN.md
new file mode 100644
index 0000000..f5ef8cf
--- /dev/null
+++ b/.planning/phases/05-regression-stability/05-02-PLAN.md
@@ -0,0 +1,101 @@
+---
+phase: 05-regression-stability
+plan: 02
+type: execute
+wave: 2
+depends_on:
+ - "05-01"
+files_modified:
+ - Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
+ - Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+ - Sources/RDReaderView/EPUBCore/RDEPUBWebView.swift
+ - Sources/RDReaderView/EPUBCore/RDEPUBWebView+FixedLayout.swift
+ - Sources/RDReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift
+ - Sources/RDReaderView/RDURLReaderController.swift
+ - ReadViewDemo/ReadViewDemo/ViewController.swift
+autonomous: true
+requirements:
+ - STAB-02
+user_setup: []
+must_haves:
+ truths:
+ - Phase 5 is allowed to fix concrete regressions discovered by the validation matrix.
+ - fixes should be evidence-driven and narrow in scope.
+ - no new architecture should be introduced in a convergence phase.
+ artifacts:
+ - .planning/phases/05-regression-stability/05-02-SUMMARY.md
+ - .planning/phases/05-regression-stability/05-VERIFICATION.md
+ key_links:
+ - `RDEPUBReaderController`
+ - `RDEPUBTextBookBuilder`
+ - `RDEPUBWebView*`
+ - `RDURLReaderController`
+---
+
+
+依据样本矩阵执行回归,定位并收敛分页、位置映射、图片/块元素分页、fixed/interactive 资源加载,以及主题切换后的稳定性问题。
+
+Purpose: 用真实样本和 reader 主流程把 Phase 1-4 的改动压测到可交付稳定水平。
+Output: regression fixes where needed, plus a verification artifact showing which main flows and sample categories passed.
+
+
+
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/05-regression-stability/05-RESEARCH.md
+@.planning/phases/05-regression-stability/05-PATTERNS.md
+@.planning/phases/05-regression-stability/05-VALIDATION.md
+@.planning/phases/03-page-metadata-pagination/03-VERIFICATION.md
+@.planning/phases/04-reader-capabilities/04-VERIFICATION.md
+@Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift
+@Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift
+@Sources/RDReaderView/EPUBCore/RDEPUBWebView.swift
+@Sources/RDReaderView/EPUBCore/RDEPUBWebView+FixedLayout.swift
+@Sources/RDReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift
+@Sources/RDReaderView/RDURLReaderController.swift
+@ReadViewDemo/ReadViewDemo/ViewController.swift
+@ReadViewDemo/ReadViewDemo/book/
+
+
+
+
+
+ Task 1: 按矩阵执行 reader 主流程回归并修复具体问题
+ Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift, Sources/RDReaderView/RDURLReaderController.swift, ReadViewDemo/ReadViewDemo/ViewController.swift
+ .planning/phases/05-regression-stability/05-VALIDATION.md, Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift, Sources/RDReaderView/RDURLReaderController.swift, ReadViewDemo/ReadViewDemo/ViewController.swift
+ 基于样本矩阵执行 native reflowable 与 TXT 相关 reader 主流程验证,包括打开、位置恢复、搜索、highlight、TOC、theme/font repagination,并对发现的具体问题做最小修复。重点是收敛错误页、状态丢失、刷新后高亮/搜索不一致、TXT 入口回退等问题。
+
+ - key native/TXT reader flows have explicit pass evidence
+ - any fixes are traceable to a concrete regression found during validation
+ - no broad architectural refactor is introduced
+
+ rg -n "search|highlight|location|restore|theme|font|bookmark|tableOfContents" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/RDURLReaderController.swift ReadViewDemo/ReadViewDemo/ViewController.swift
+ native and TXT reader flows are regression-checked and stabilized.
+
+
+
+ Task 2: 回归 fixed / interactive WebKit 路径并收敛资源加载与展示问题
+ Sources/RDReaderView/EPUBCore/RDEPUBWebView.swift, Sources/RDReaderView/EPUBCore/RDEPUBWebView+FixedLayout.swift, Sources/RDReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift, ReadViewDemo/ReadViewDemo/ViewController.swift
+ Sources/RDReaderView/EPUBCore/RDEPUBWebView.swift, Sources/RDReaderView/EPUBCore/RDEPUBWebView+FixedLayout.swift, Sources/RDReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift, ReadViewDemo/ReadViewDemo/ViewController.swift
+ 对 fixed / interactive EPUB 路径做回归验证,利用现有 `RDEPUBWebViewDebug` 与 demo/runtime evidence 检查资源加载、页面渲染、外链/内部链接与 search decoration 相关行为。若发现明显回归(白屏、加载失败、关键资源缺失、渲染未完成),做最小修复并把结果写入 verification artifact。
+
+ - fixed or interactive sample path has explicit runtime evidence
+ - resource loading and rendering regressions are either fixed or clearly surfaced in verification
+ - demo/runtime logs remain sufficient to inspect failures without adding a second validation shell
+
+ rg -n "RDEPUBWebViewDebug|load fixed|ResourceScheme|rendered|didFail|didFinish|searchPresentation" Sources/RDReaderView/EPUBCore ReadViewDemo/ReadViewDemo -S
+ WebKit-backed reader paths are included in the final regression closure, not treated as out-of-band.
+
+
+
+
+
+Before declaring plan complete:
+- [ ] `find ReadViewDemo/ReadViewDemo/book -maxdepth 1 -type f | sort`
+- [ ] `rg -n "search|highlight|location|restore|theme|font|bookmark|tableOfContents" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/RDURLReaderController.swift ReadViewDemo/ReadViewDemo/ViewController.swift`
+- [ ] `rg -n "RDEPUBWebViewDebug|load fixed|ResourceScheme|rendered|didFail|didFinish|searchPresentation" Sources/RDReaderView/EPUBCore ReadViewDemo/ReadViewDemo -S`
+
+
+
diff --git a/.planning/phases/05-regression-stability/05-02-SUMMARY.md b/.planning/phases/05-regression-stability/05-02-SUMMARY.md
new file mode 100644
index 0000000..1b197b0
--- /dev/null
+++ b/.planning/phases/05-regression-stability/05-02-SUMMARY.md
@@ -0,0 +1,16 @@
+# 05-02 Summary
+
+## Outcome
+
+Wave 2 closed the remaining regression gap by exercising the new matrix and tightening the demo validator where it initially drifted from public APIs.
+
+- fixed/interactive validation now resolves spine resources through public `RDEPUBResourceResolver` APIs and verifies fixed spreads with explicit `RDEPUBPreferences`
+- TXT validation now builds a real `RDPlainTextBook` and records chapter/page evidence
+- the existing native pagination and restore diagnostics remain active for reflowable books, so Phase 3-4 guarantees stay visible during Phase 5 runs
+
+## Fixes
+
+- removed demo-only calls to internal reader helpers (`makePreferences`, theme CSS accessors)
+- removed the nonisolated `UIScreen.main` warning by passing the existing viewport size into fixed-layout validation
+- kept the changes local to `ReadViewDemo/ReadViewDemo/ViewController.swift`; no new runtime architecture was introduced
+
diff --git a/.planning/phases/05-regression-stability/05-PATTERNS.md b/.planning/phases/05-regression-stability/05-PATTERNS.md
new file mode 100644
index 0000000..95a45c4
--- /dev/null
+++ b/.planning/phases/05-regression-stability/05-PATTERNS.md
@@ -0,0 +1,27 @@
+# Phase 5: 回归验证与稳定性收敛 - Pattern Map
+
+## Goal
+
+把 Phase 5 约束成“样本矩阵 + reader 流程 + 收敛修复”的闭环,而不是泛泛地补测试。
+
+## Planned Outputs
+
+| Planned file | Role | Primary evidence | Why this is the right analog |
+|--------------|------|------------------|------------------------------|
+| `05-01-PLAN.md` | 构建样本矩阵、诊断手段和复现清单 | `ReadViewDemo/ViewController.swift`, `ReadViewDemo/book/`, `RDEPUBWebViewDebug`, prior verification artifacts | 这些是现有验证入口和日志基础。 |
+| `05-02-PLAN.md` | 依据矩阵执行回归并收敛稳定性问题 | `RDEPUBReaderController`, `RDEPUBTextBookBuilder`, `RDEPUBWebView*`, `RDURLReaderController` | 这里汇聚了 Phase 1-4 改动后的主要风险表面。 |
+
+## Code Evidence Map
+
+| Concern | Closest source of truth | Evidence to extract |
+|---------|-------------------------|---------------------|
+| 样本入口与启动验证 | `ReadViewDemo/ReadViewDemo/ViewController.swift` | 已有 startup validation,可扩展成矩阵摘要。 |
+| native reflowable 风险 | `RDEPUBReaderController`, `RDEPUBTextBookBuilder`, `RDEPUBTextContentView` | 搜索、高亮、repagination、位置恢复都集中在这里。 |
+| fixed / interactive 风险 | `RDEPUBWebView`, `RDEPUBWebView+FixedLayout`, `RDEPUBResourceURLSchemeHandler` | 这些路径依赖 WebKit/resource loading,不同于 native text。 |
+| TXT 支持风险 | `RDURLReaderController`, `RDPlainTextBookBuilder` | roadmap 要求 `.txt` 主流程不回归,但当前样本覆盖不足。 |
+
+## Writing Guidance
+
+- Phase 5 的计划必须明确区分“验证基础设施/矩阵”与“发现问题后的收敛修复”。
+- 任何修复都应以回归证据驱动,不应重新打开更大范围的架构重构。
+- 计划里要写清楚哪些样本书承担哪些验证职责。
diff --git a/.planning/phases/05-regression-stability/05-RESEARCH.md b/.planning/phases/05-regression-stability/05-RESEARCH.md
new file mode 100644
index 0000000..6cbd7dd
--- /dev/null
+++ b/.planning/phases/05-regression-stability/05-RESEARCH.md
@@ -0,0 +1,122 @@
+# Phase 5: 回归验证与稳定性收敛 - Research
+
+**Researched:** 2026-05-22
+**Domain:** iOS EPUB/TXT regression validation / sample-corpus coverage / stability convergence
+**Confidence:** HIGH
+
+
+## User Constraints
+
+No `CONTEXT.md` exists for this phase. Planning is based on roadmap requirements, the current codebase, and completed artifacts from Phases 1-4 only.
+
+
+
+## Summary
+
+By the end of Phase 4, the codebase has meaningful diagnostics for both native text pagination and reader-state restore, but the validation surface is still uneven. Most current proof is either:
+
+- source inspection (`rg`, architecture invariants)
+- simulator build/run success
+- demo startup logs from a small subset of sample books
+
+That was enough to unblock development, but not enough to claim stability closure. Phase 5 therefore shifted from “implement capability” to “prove breadth and harden failures.”
+
+The sample corpus used for execution now covers five local books:
+
+- `《凡人修仙传》精校版全本.epub`
+- `回归验证样本.txt`
+- `宝山辽墓材料与释读.epub`
+- `张学良传.epub`
+- `爱忘事的熊爷爷.epub`
+
+Observed behavior from Phases 3-4 shows these books are not equivalent:
+
+- `宝山辽墓材料与释读.epub` exercises native reflowable pagination semantics and repagination restore checks
+- `爱忘事的熊爷爷.epub` clearly drives fixed-layout / interactive-style WebKit resource flows in runtime logs
+- `回归验证样本.txt` was added during execution so the TXT path is now part of the built-in demo corpus
+
+The main Phase 5 planning implication is that validation has to be organized as a matrix, not a single “open the app and see it works” loop. At minimum the matrix must cover:
+
+1. Native reflowable text with richer page metadata and repagination
+2. Fixed layout / WebKit-based rendering and resource loading
+3. TXT/native text book path through `RDURLReaderController`
+4. Reader interaction flows: open, TOC jump, search navigation, highlight persistence, theme/font changes, and repeated repagination
+
+The repo already has enough hooks to support this:
+
+- `ReadViewDemo/ViewController.swift` reports sample validation summaries at startup
+- `RDEPUBTextBookBuilder` records pagination/resource diagnostics
+- `RDEPUBWebViewDebug` and resource-scheme logs expose WebKit/fixed-layout loading behavior
+- `RDEPUBReaderController` centralizes repagination, restore, search, highlight, and viewport handling
+
+What is missing is orchestration and closure:
+
+- explicit mapping from sample book → risk category
+- runtime assertions or summary output for reader flows beyond startup validation
+- a disciplined path to take discovered regressions and either fix them or record them as resolved in the same phase
+
+**Primary recommendation:** Plan Phase 5 as a validation-and-fix loop centered on a documented sample matrix and runtime evidence. The goal is not new architecture; it is repeatable proof that the main supported book categories and reader flows stay intact after the Phase 1-4 refactor.
+
+Execution outcome:
+
+- `ReadViewDemo` startup validation now emits matrix lines for complex reflowable, fixed/interactive WebKit, and TXT samples
+- runtime evidence showed `样本验证:5/5 通过`
+- the demo remains the single validation entry point; no secondary harness was introduced
+
+
+
+## Code Evidence
+
+| Concern | Source | Why it matters |
+|--------|--------|----------------|
+| Demo validation entry point | `ReadViewDemo/ReadViewDemo/ViewController.swift` | Already emits startup resource/pagination/restore summaries; best place to extend verification. |
+| Native text diagnostics | `RDEPUBTextBookBuilder`, `RDEPUBTextLayouter`, `RDEPUBTextRendererSupport` | Existing metadata can drive stronger regression assertions. |
+| Reader flow behavior | `RDEPUBReaderController` | Open, search, selection, highlight, TOC, repagination, and state restore all converge here. |
+| Web/fixed runtime logs | `RDEPUBWebViewDebug`, `RDEPUBWebView`, `RDEPUBWebView+FixedLayout`, `RDEPUBResourceURLSchemeHandler` | Needed to prove non-native paths did not regress. |
+| TXT path | `RDURLReaderController`, `RDPlainTextBookBuilder` | Supported but currently underrepresented in the sample corpus. |
+
+
+
+
+## Current Sample Corpus
+
+| Sample | Likely category | Current evidence |
+|--------|------------------|------------------|
+| `宝山辽墓材料与释读.epub` | Native reflowable, complex blocks/images | Used in Phase 3/4 pagination and restore diagnostics. |
+| `《凡人修仙传》精校版全本.epub` | Interactive/WebKit path | Runtime summary now classifies it as `webInteractive` and verifies `spine 2470 · missing 0`. |
+| `回归验证样本.txt` | TXT/native text path | Added in Phase 5 and validated through `RDPlainTextBookBuilder` with startup summary output. |
+| `张学良传.epub` | Reflowable nonfiction / alternate structure | Runtime summary classifies it under complex reflowable. |
+| `爱忘事的熊爷爷.epub` | Fixed-layout / interactive-style WebKit path | Runtime log shows extensive fixed/resource loading. |
+
+
+
+
+## Architecture Patterns
+
+### Pattern 1: Validate by book category, not by implementation layer
+**What:** Organize checks around sample types and user flows.
+**Why:** Stability issues are user-visible regressions, not just module-level failures.
+
+### Pattern 2: Prefer evidence-producing diagnostics over ad hoc manual notes
+**What:** Runtime summaries, assertions, and structured logs should back regression claims.
+**Why:** Repeatability matters more than one-off successful runs.
+
+### Pattern 3: Couple regression detection with fix closure
+**What:** The phase should allow targeted source fixes where diagnostics surface concrete failures.
+**Why:** A pure audit phase without remediation would leave the roadmap unfinished.
+
+### Anti-Patterns to Avoid
+- Treating build success as sufficient evidence of reader stability.
+- Adding broad new architecture during a convergence phase.
+- Expanding validation without documenting which sample proves which risk category.
+
+
+
+
+## Open Questions
+
+- Should Phase 5 add a dedicated TXT sample into the demo corpus, or rely on an external local text file during execution?
+- How much of the reader interaction validation should be automated in code/logs versus performed manually on simulator?
+- If a regression is found in fixed/interactive WebKit flows, should it be fixed within Phase 5 or only documented if the root cause is outside the current scope?
+
+
diff --git a/.planning/phases/05-regression-stability/05-VALIDATION.md b/.planning/phases/05-regression-stability/05-VALIDATION.md
new file mode 100644
index 0000000..205f839
--- /dev/null
+++ b/.planning/phases/05-regression-stability/05-VALIDATION.md
@@ -0,0 +1,46 @@
+# Phase 5: 回归验证与稳定性收敛 - Validation
+
+## Required Checks
+
+- 至少覆盖三类样本:纯文本/TXT 或小说型 reflowable、复杂块元素/图片 reflowable、fixed/interactive WebKit 路径
+- `.epub` 与 `.txt` 的 reader 主流程都必须有明确验证路径
+- 每个重要 reader 流程至少有一种可重复证据:日志、断言、摘要、或稳定的手动检查清单
+
+## Executed Matrix
+
+| Sample | Category | Evidence |
+|--------|----------|----------|
+| `宝山辽墓材料与释读.epub` | 复杂图文 / native reflowable | 启动摘要 + pagination/restore diagnostics |
+| `张学良传.epub` | 复杂图文 / native reflowable | 启动摘要 + active native reader UI evidence |
+| `《凡人修仙传》精校版全本.epub` | interactive / WebKit | 启动摘要 `webInteractive` + `missing 0` |
+| `爱忘事的熊爷爷.epub` | fixed layout / WebKit | 启动摘要 `webFixedLayout` + `spread` validation |
+| `回归验证样本.txt` | TXT | 启动摘要 `TXT 验证` + page/chapter counts |
+
+## Verification Anchors
+
+### Source-level
+- `find ReadViewDemo/ReadViewDemo/book -maxdepth 1 -type f | sort`
+- `rg -n "validation|diagnostic|restore|search|highlight|location|theme|font|fixed|resource" ReadViewDemo/ReadViewDemo Sources/RDReaderView -S`
+- `rg -n "RDEPUBWebViewDebug|logSchemeTask|rendered|page break|restore" Sources/RDReaderView -S`
+
+### Runtime / demo
+- run `ReadViewDemo` on simulator
+- capture startup validation summaries for every relevant sample category
+- exercise at least one native reflowable flow with search/highlight/repagination
+- exercise at least one fixed/interactive EPUB flow and confirm no loading/resource regression
+- exercise a TXT reading flow if a sample exists or is added during execution
+
+## Current Rerun Path
+
+1. Launch `ReadViewDemo` on the configured simulator.
+2. Read the startup summary from the on-screen status area or runtime log.
+3. Confirm the matrix lines for `复杂图文`、`Fixed/互动`、`TXT`.
+4. Open any listed sample and use the existing reader controls to recheck TOC, bookmark/highlight, and theme/font adjustments as needed.
+
+## Completion Standard
+
+Phase 5 planning is only good enough if each plan names:
+
+1. which sample categories it covers
+2. what evidence proves success
+3. how discovered regressions are routed into concrete fixes
diff --git a/.planning/phases/05-regression-stability/05-VERIFICATION.md b/.planning/phases/05-regression-stability/05-VERIFICATION.md
new file mode 100644
index 0000000..6d0c6f2
--- /dev/null
+++ b/.planning/phases/05-regression-stability/05-VERIFICATION.md
@@ -0,0 +1,47 @@
+# Phase 5 Verification
+
+## Commands
+
+- `build_sim` for scheme `ReadViewDemo`
+- `build_run_sim` for scheme `ReadViewDemo`
+- `find ReadViewDemo/ReadViewDemo/book -maxdepth 1 -type f | sort`
+- `rg -n "validation|diagnostic|sample|matrix|restore|fixed|reflowable|txt" ReadViewDemo/ReadViewDemo .planning/phases/05-regression-stability -S`
+- `rg -n "search|highlight|location|restore|theme|font|bookmark|tableOfContents" Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift Sources/RDReaderView/RDURLReaderController.swift ReadViewDemo/ReadViewDemo/ViewController.swift`
+- `rg -n "RDEPUBWebViewDebug|load fixed|ResourceScheme|rendered|didFail|didFinish|searchPresentation" Sources/RDReaderView/EPUBCore ReadViewDemo/ReadViewDemo -S`
+- `snapshot_ui` on the running simulator
+
+## Results
+
+- Simulator build succeeded with no warnings
+- Simulator launch succeeded
+- Demo corpus now includes 5 verifiable books:
+ - `《凡人修仙传》精校版全本.epub`
+ - `回归验证样本.txt`
+ - `宝山辽墓材料与释读.epub`
+ - `张学良传.epub`
+ - `爱忘事的熊爷爷.epub`
+- Source verification confirms:
+ - `ViewController` now validates `.txt`, native reflowable EPUB, and fixed/interactive EPUB in one pass
+ - native reader flows for search, highlight, location restore, bookmarks, TOC, and theme/font repagination remain centered in `RDEPUBReaderController`
+ - WebKit-backed flows still expose resource/render lifecycle evidence through `RDEPUBWebViewDebug` and `RDEPUBResourceURLSchemeHandler`
+
+## Runtime Evidence
+
+From the app runtime log:
+
+- `样本验证:5/5 通过`
+- `矩阵[复杂图文] 2/2 · 宝山辽墓材料与释读(textReflowable)、张学良传(textReflowable)`
+- `矩阵[Fixed/互动] 2/2 · 《凡人修仙传》精校版全本(webInteractive)、爱忘事的熊爷爷(webFixedLayout)`
+- `矩阵[TXT] 1/1 · 回归验证样本(txt)`
+- `Web 路径验证:《凡人修仙传》精校版全本 · profile webInteractive · spine 2470 · missing 0`
+- `Web 路径验证:爱忘事的熊爷爷 · profile webFixedLayout · spine 22 · missing 0`
+
+From simulator UI inspection:
+
+- the app was running inside `RDEPUBReaderController` with visible reader controls (`epub.reader.back`, `epub.reader.toc`, `epub.reader.bookmarks`, `epub.reader.highlights`, `epub.reader.settings`)
+- the current native reflowable sample exposed page state (`2 / 691`) and standard reader chrome without a blank page or stalled render
+
+## Residual Risk
+
+- The phase now has corpus-level runtime evidence for TXT and WebKit paths, but not a fully automated UI walkthrough for opening every sample and driving every interaction on simulator
+- Simulator runtime still emits pre-existing CoreText font substitution notes for `.SFUI-Semibold`; Phase 5 did not change font selection code and the app remained functional despite the warning
diff --git a/CONTEXT.md b/CONTEXT.md
new file mode 100644
index 0000000..ace1000
--- /dev/null
+++ b/CONTEXT.md
@@ -0,0 +1,19 @@
+# Project Context & Rules
+
+## Language Specification
+- **Planning & Documentation:** All plans, task lists, and specifications generated inside the `.planning/` directory MUST be written in Chinese.
+- **Project Docs:** All documents generated or updated under `Doc/`, project root Markdown documentation, and GSD-produced delivery/analysis documents MUST be written in Chinese.
+- **Codebase:** Source code syntax (class names, function names, variables, file names, API identifiers, route identifiers) must remain in English, but all code comments, documentation (Docstrings), and commit messages MUST be written in Chinese.
+
+## Agent / Sub-agent Enforcement (GSD & Codex)
+- **Mandatory pre-read:** Any agent/sub-agent that generates or updates files MUST read this file (`CONTEXT.md`) first and follow it as the source-of-truth for language/style constraints.
+- **Chinese-only outputs:** Any artifact written under `.planning/` (including `.planning/codebase/*.md`) MUST be Chinese. Do not output English headings/sections unless the content is a literal code identifier or a proper noun that should not be translated.
+- **When spawning sub-agents:** The orchestrator MUST either:
+ - set `fork_context=true`, AND
+ - explicitly instruct the agent to read `CONTEXT.md` before writing; OR
+ - (if `fork_context=false`) explicitly include the above constraints in the sub-agent prompt and still require a `CONTEXT.md` pre-read.
+
+### Recommended prompt snippet (copy into sub-agent tasks)
+1) First read `CONTEXT.md` and follow all rules.
+2) All markdown you write under `.planning/` MUST be Chinese.
+3) Keep code identifiers / file paths / commands in English as-is.
diff --git a/Doc/FeatureSolution/ReflowableEPUB_WXReadRenderer_Design.md b/Doc/FeatureSolution/ReflowableEPUB_WXReadRenderer_Design.md
index 1a11bb2..61e1e88 100644
--- a/Doc/FeatureSolution/ReflowableEPUB_WXReadRenderer_Design.md
+++ b/Doc/FeatureSolution/ReflowableEPUB_WXReadRenderer_Design.md
@@ -34,6 +34,8 @@ ReadViewSDK 当前对 EPUB 有三类渲染路径:
## 2. 关键事实核验(当前代码真实路径)
+> 纠偏说明:`.planning/PROJECT.md` 在初始化时曾把“reflowable EPUB 当前路径”概括为偏 `WKWebView` 的历史性表述。经本次代码核验,当前真实主路径是 `.textReflowable` → `RDEPUBTextBookBuilder` → `RDEPUBDTCoreTextRenderer` → CoreText 分页 → `RDEPUBTextContentView`。本设计以代码事实为准,并默认后续计划/实现都按此理解推进。
+
### 2.1 渲染路径分流(已存在)
- 判定在 `Sources/RDReaderView/EPUBCore/RDEPUBParser+ReadingProfile.swift`:
@@ -69,6 +71,12 @@ ReadViewSDK 当前对 EPUB 有三类渲染路径:
- B:**资源解析**(图片/CSS 的相对路径 baseURL)与稳定性保障
- C:在现有分页基础上逐步迭代(先可用,后对齐“避免断页”等高级策略)
+### 3.1 第一阶段实施假设(必须遵守)
+
+- H1:**第一期只对齐“管线形态”和“CSS 分层策略”**,即把现有 `.textReflowable` renderer 增强为更接近 WXRead 的 typesetter 输入与样式组织方式。
+- H2:**第一期不实现 WXRead 对 DTCoreText 的深度魔改**,包括但不限于自定义 CSS 属性体系、复杂附件布局规则、完整的 `WRCoreTextLayouter` / `WRCoreTextLayoutFrame` 等价分页器。
+- H3:当开发过程中遇到图片断页、复杂样式缺失、附件布局异常等问题时,默认先作为“第二阶段问题清单”记录;只有在它阻塞 `REND-01` / `STAB-02` 的最小验收时,才允许做局部补丁,而不是扩展为全面重写分页引擎。
+
## 4. 是否能直接使用 Doc/WXRead 中的 JS/CSS?
结论:**不建议、也不应该直接把“来自微信读书 App bundle 的私有 JS/CSS”拷贝进 SDK 作为产品代码**;但可以按以下原则“选择性使用”:
@@ -169,6 +177,8 @@ WXRead 的更高阶策略(参考 `Doc/WXRead/analysis/DTCoreText自定义修
- Phase 2:先保持现有分页算法,只要渲染输入(CSS 分层 + 后处理)到位,就能显著改善一致性。
- Phase 3:针对真实书籍出现的问题,逐条补齐分页规则(问题驱动),避免一开始就引入复杂分页器导致风险扩大。
+> 范围约束:如果某个分页问题需要引入“新的复杂分页器”或大规模模拟 `WRCoreTextLayouter` / `WRCoreTextLayoutFrame`,应先暂停并回到方案讨论,不默认并入第一期实现。
+
### 5.5 与现有高亮/搜索/位置映射的兼容
当前 `.textReflowable` 路径:
@@ -224,7 +234,11 @@ WXRead 的更高阶策略(参考 `Doc/WXRead/analysis/DTCoreText自定义修
### 对应 STAB-01 / STAB-02
- `RDURLReaderController` 打开 `.epub` / `.txt` 主流程不回归
-- 2-3 本典型 reflowable EPUB:不崩溃、不白屏、不无限加载;分页/翻页可用
+- 至少使用以下 3 类 reflowable EPUB 样本进行回归:
+ - 样本 A:纯文本/小说类章节为主,验证基础段落、标题、分页与阅读位置恢复
+ - 样本 B:包含内嵌图片与多段样式的章节,验证图片显示、图片前后分页、基础 CSS 生效
+ - 样本 C:包含外链与多个 CSS 文件引用的章节,验证 baseURL、样式解析与链接呈现稳定性
+- 对以上样本的共同要求:不崩溃、不白屏、不无限加载;分页/翻页可用
## 8. 风险清单与降级策略
diff --git a/Doc/FeatureSolution/书签能力方案讨论.md b/Doc/FeatureSolution/书签能力方案讨论.md
deleted file mode 100644
index fb771c6..0000000
--- a/Doc/FeatureSolution/书签能力方案讨论.md
+++ /dev/null
@@ -1,398 +0,0 @@
-# 书签能力方案讨论
-
-## 需求背景
-
-`Doc/EPUB_MAINTENANCE.md` 已将“书签能力”列为 EPUB 阅读器下一阶段的高优先级能力之一。
-
-当前阅读器已经具备:
-
-1. EPUB 打开与解析
-2. WebView / DTCoreText / fixed-layout 三条正文渲染路径
-3. 目录跳转、阅读位置恢复、主题与字号调整
-4. 高亮、划线、批注、搜索等阅读交互能力
-
-但现阶段“位置记录”仍主要依赖:
-
-1. 自动保存的上次阅读位置
-2. 需要选中文本的高亮 / 批注能力
-
-这会遗漏一种非常常见、也非常轻量的阅读诉求:
-
-1. 只想标记当前位置
-2. 稍后回来继续读
-3. 不需要摘录正文
-4. 不需要输入批注
-
-因此,“书签能力”这项需求的目标,不是扩展现有标注功能,而是补上一条面向“位置收藏”的轻量链路,用来覆盖“只记录位置、不做摘录”的常见阅读需求。
-
-## 代码事实
-
-### 已有基础
-
-当前仓库其实已经具备不少可直接复用的基础设施:
-
-1. `RDEPUBLocation` 已能稳定表达 `href + progression + fragment`
-2. `RDEPUBReaderController` 已具备“当前位置读取”和“跳转到位置”的主链路
-3. `RDEPUBReaderPersistence` 已形成按 `bookIdentifier` 分书持久化的模式
-4. 高亮管理页已经具备“列表展示 -> 点击跳转 -> 删除”的成熟交互范式
-
-结论:
-
-1. 书签不需要重新设计位置模型
-2. 书签可以复用现有定位和跳转能力
-3. 更需要新增的是独立模型、持久化存储位和 UI 入口
-
-### 当前实现存在的问题
-
-#### 问题 1:当前只有单一阅读位置,没有“多书签”能力
-
-`RDEPUBReaderPersistence` 当前只提供:
-
-1. `loadLocation / saveLocation`
-2. `loadHighlights / saveHighlights`
-3. `loadReaderSettings / saveReaderSettings`
-
-这意味着:
-
-1. 系统只能保存“上次读到哪”
-2. 不能保存“用户主动收藏的多个位置”
-
-而这两者的语义并不相同:
-
-1. 阅读位置是自动覆盖的
-2. 书签是主动创建、可长期保留多个的
-
-#### 问题 2:当前标注模型不适合直接承载书签
-
-虽然看起来可以用 `RDEPUBHighlight` 勉强模拟书签,但这会带来明显问题:
-
-1. 书签不一定有选中文本
-2. 书签不应依赖 `rangeInfo`
-3. 书签列表应以“位置”为中心,而不是以“摘录内容”为中心
-4. 书签与高亮、划线、批注的展示和管理语义不同
-
-因此,不建议把书签硬塞进现有高亮模型。
-
-#### 问题 3:当前 UI 没有书签入口与状态反馈
-
-从 `RDEPUBReaderBottomToolView` 当前结构来看,只有:
-
-1. 目录
-2. 批注列表
-3. 添加标注
-4. 设置
-
-当前并没有:
-
-1. 当前页添加 / 取消书签入口
-2. 书签列表入口
-3. 当前阅读位置是否已加书签的状态反馈
-
-这意味着即便补了数据层,用户仍然无法自然感知和使用这项能力。
-
-## 需求拆解
-
-建议把“书签能力”拆成四部分推进。
-
-### 1. 独立书签模型
-
-需要新增一个面向“位置记录”的独立模型,而不是复用高亮模型。
-
-### 2. 独立书签持久化
-
-需要新增书签读写接口,支持每本书存储多个书签。
-
-### 3. 书签入口与状态反馈
-
-需要让用户在当前页能轻量创建 / 取消书签,并能看到当前位置的书签状态。
-
-### 4. 书签管理与跳转
-
-需要补齐书签列表、点击跳转、删除等基本管理闭环。
-
-## 技术路线对比
-
-### 路线 A:把书签并入 `RDEPUBHighlight`
-
-思路:
-
-1. 给 `RDEPUBHighlight` 增加一个新的 style 或特殊标识
-2. 将书签存入 highlights
-3. 复用现有标注列表页
-
-优点:
-
-1. 表面上改动范围较小
-2. 可以快速借用现有列表管理代码
-
-风险:
-
-1. 模型语义不清晰
-2. 书签没有文本选区时会很别扭
-3. 后续展示、筛选、持久化逻辑会越来越绕
-4. 容易让“书签”和“标注”边界变得混乱
-
-### 路线 B:完全独立一条书签链路
-
-思路:
-
-1. 新建 `RDEPUBBookmark`
-2. 新增 `loadBookmarks / saveBookmarks`
-3. 新增书签列表页
-4. 独立接入添加、删除、跳转、状态更新
-
-优点:
-
-1. 模型语义清晰
-2. 更符合产品能力边界
-3. 后续扩展空间更好
-
-风险:
-
-1. 会新增一部分 UI 与管理代码
-2. 与高亮列表在形态上会有一定重复
-
-### 路线 C:模型独立,交互模式局部复用
-
-思路:
-
-1. 数据模型与持久化独立
-2. 定位、跳转和列表交互模式尽量复用现有高亮链路
-3. 第一阶段先做轻量闭环,再考虑更复杂管理能力
-
-优点:
-
-1. 语义清晰,同时控制改动范围
-2. 能复用现有阅读位置链路和管理交互经验
-3. 更适合当前仓库的演进方式
-
-风险:
-
-1. 仍需补一组新的数据与列表代码
-2. 需要明确书签状态判定规则,避免重排后误判
-
-### 推荐结论
-
-建议采用:
-
-**主路线:路线 C,模型独立,位置链路复用,UI 先做轻量闭环**
-
-原因:
-
-1. 书签和标注的语义不同,模型应保持独立
-2. 当前仓库已有成熟的位置和跳转主链路,可以直接复用
-3. 第一阶段先满足“记录位置”的核心需求,不需要把书签做得过重
-
-## 推荐方案
-
-### 方案总览
-
-书签能力第一阶段建议形成如下结构:
-
-1. 新增 `RDEPUBBookmark` 独立模型
-2. 扩展 `RDEPUBReaderPersistence`,新增 bookmarks 读写
-3. 在 `RDEPUBReaderController` 中接入书签状态、添加、删除、跳转能力
-4. 增加轻量级书签入口和书签列表页
-5. 支持退出重进后的书签持久化恢复
-
-### 推荐数据模型
-
-建议书签模型至少包含以下字段:
-
-1. `id`
-2. `bookIdentifier`
-3. `location`
-4. `chapterTitle`
-5. `displayText` 或摘要信息(可选)
-6. `note`(可选)
-7. `createdAt`
-
-其中第一阶段真正必要的最小集合是:
-
-1. `id`
-2. `bookIdentifier`
-3. `location`
-4. `createdAt`
-
-建议同时补上 `chapterTitle`,这样书签列表在展示时会更友好,也更接近真实阅读器使用习惯。
-
-### 推荐持久化结构
-
-建议在 `RDEPUBReaderPersistence` 中新增:
-
-1. `loadBookmarks(for:)`
-2. `saveBookmarks(_:for:)`
-
-`RDEPUBUserDefaultsPersistence` 中新增:
-
-1. `bookmarksPrefix`
-
-第一阶段继续沿用当前:
-
-1. `UserDefaults`
-2. `JSONEncoder / JSONDecoder`
-3. `bookIdentifier` 作用域隔离
-
-这样能在最小改动下形成可用闭环。
-
-### 推荐 UI 入口
-
-第一阶段建议至少补两类入口:
-
-1. 当前页添加 / 取消书签入口
-2. 书签列表入口
-
-比较自然的放置方式是:
-
-1. 顶部工具栏增加“当前页书签”切换按钮
-2. 底部工具栏增加“书签列表”入口
-
-这样可以把两种动作区分开:
-
-1. “加书签”是当前上下文动作
-2. “看书签列表”是全局管理动作
-
-### 推荐交互闭环
-
-第一阶段建议用户路径收敛成下面这条主链路:
-
-1. 用户在当前阅读位置点击书签按钮
-2. 如果当前语义位置未加书签,则创建书签
-3. 如果当前语义位置已加书签,则取消该书签
-4. 用户可从书签列表查看当前书籍的全部书签
-5. 点击任意书签后跳转到对应位置
-6. 支持从列表中删除书签
-
-## 关键实现建议
-
-### Task K1:新增书签模型
-
-文件:
-
-1. `Sources/RDReaderView/EPUBCore/RDEPUBReadingModels.swift`
-
-改动:
-
-1. 新增 `RDEPUBBookmark: Codable, Equatable`
-2. 字段以 `location` 为核心,不依赖选区文本
-
-完成定义:
-
-1. 可以表达一本书中的一个可持久化书签位置
-
-### Task K2:扩展书签持久化协议
-
-文件:
-
-1. `Sources/RDReaderView/EPUBUI/RDEPUBReaderPersistence.swift`
-
-改动:
-
-1. 协议增加 `loadBookmarks / saveBookmarks`
-2. `RDEPUBUserDefaultsPersistence` 增加 `bookmarksPrefix`
-
-完成定义:
-
-1. 每本书可以稳定读取和保存多个书签
-
-### Task K3:控制器接入书签状态与操作
-
-文件:
-
-1. `Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`
-
-改动:
-
-1. 增加 `activeBookmarks`
-2. 打开图书时加载 bookmarks
-3. 增加 `addBookmark`
-4. 增加 `removeBookmark`
-5. 增加 `toggleBookmark`
-6. 增加 `go(to: bookmark)`
-7. 增加当前阅读位置是否已书签的状态计算
-
-完成定义:
-
-1. 书签与阅读主链路、跳转链路、持久化链路打通
-
-### Task K4:补书签 UI 与列表管理
-
-文件:
-
-1. `Sources/RDReaderView/EPUBUI/RDEPUBReaderBottomToolView.swift`
-2. 新增 `Sources/RDReaderView/EPUBUI/RDEPUBReaderBookmarksViewController.swift`
-3. 视情况补充顶部工具栏按钮
-
-改动:
-
-1. 添加书签入口
-2. 添加书签列表入口
-3. 点击书签跳转
-4. 支持删除
-5. 提供当前位置书签状态反馈
-
-完成定义:
-
-1. 用户能看到、添加、取消、查看和管理书签
-
-## 当前页书签判定建议
-
-“当前位置是否已加书签”不建议简单按“完全相同的 `progression`”判断,因为:
-
-1. 字号变化会触发重分页
-2. 视口尺寸变化会导致 progression 细微漂移
-3. WebView 和文本分页的进度精度并不完全一致
-
-更合理的判定策略是:
-
-1. 优先比较标准化后的 `href`
-2. 如果存在 `fragment`,优先使用 `fragment` 作为更强锚点
-3. reflowable 路径允许一定 progression 容差
-4. fixed-layout 路径优先按页级位置判断
-
-这样可以减少用户在重排、重进或轻微位置变化时的书签状态抖动。
-
-## 验收矩阵建议
-
-第一阶段至少要覆盖以下验证维度:
-
-1. 当前页可添加书签
-2. 同一位置可取消书签
-3. 书签列表展示正常
-4. 点击书签可以跳转
-5. 删除书签后列表更新正确
-6. 退出重进后书签仍存在
-7. 字号变化后书签仍能回到同章节或邻近语义位置
-8. `webInteractive` 路径正常
-9. `textReflowable` 路径正常
-10. `webFixedLayout` 路径至少能按页恢复
-
-## 风险与注意点
-
-### 风险 1:把书签与高亮强行合并
-
-这样短期看似省事,长期会让模型、持久化和 UI 语义都变重。
-
-### 风险 2:过度依赖精确 progression 匹配
-
-如果书签命中规则太严格,重分页后会显得“不稳定”,用户体验会很差。
-
-### 风险 3:第一阶段把书签做得过重
-
-书签能力的首要目标是“轻量记录位置”,不是一开始就做成复杂的收藏管理系统。
-
-## 推荐实施顺序
-
-如果目标是“最小投入换最大需求闭环”,建议按下面顺序推进:
-
-1. 先补独立 `RDEPUBBookmark` 模型
-2. 先扩展 `RDEPUBReaderPersistence` 的书签读写能力
-3. 再在 `RDEPUBReaderController` 接入书签主链路
-4. 最后补 UI 入口和书签列表页
-5. 待第一阶段稳定后,再考虑备注、排序、筛选等增强能力
-
-## 推荐结论
-
-“书签能力”这项需求,建议最终收敛为下面这句话:
-
-**为 EPUB 阅读器补齐一条独立的轻量书签链路,以 `RDEPUBLocation` 为核心记录位置,支持添加、取消、列表管理、跳转与持久化恢复,用来覆盖“只记录位置、不做摘录”的常见阅读需求。**
diff --git a/Doc/FeatureSolution/样书基线验证方案讨论.md b/Doc/FeatureSolution/样书基线验证方案讨论.md
deleted file mode 100644
index 73ba068..0000000
--- a/Doc/FeatureSolution/样书基线验证方案讨论.md
+++ /dev/null
@@ -1,439 +0,0 @@
-# 样书基线验证方案讨论
-
-## 需求背景
-
-`Doc/EPUB_MAINTENANCE.md` 已将“样书基线验证”列为 EPUB 阅读器下一阶段的高优先级能力之一。
-
-当前阅读器已经具备:
-
-1. EPUB 打开与解析
-2. WebView / DTCoreText / fixed-layout 三条正文渲染路径
-3. 目录跳转、阅读位置恢复、字号与主题调整
-4. 高亮、划线、批注、搜索等阅读交互能力
-
-但这些能力目前主要还是依赖“人手点一遍”来判断是否正常,缺少固定样书、固定流程、固定输出格式的基线验证机制。
-
-因此,“样书基线验证”这项需求的目标,不是再增加几本 demo 书,而是基于当前已内置的四本 EPUB 样书,建立一套:
-
-1. 可重复执行
-2. 可横向比较
-3. 可长期沉淀
-4. 可逐步自动化
-
-的阅读器基线验证机制。
-
-## 代码事实
-
-### 已有基础
-
-当前仓库已经具备样书基线验证所需的基本条件:
-
-1. `ReadViewSDKDemo/ReadViewSDKDemo/Resources/` 内已经内置四本 EPUB 样书
-2. `RDReaderManager.bundledEPUBURLs(in:)` 会自动枚举 bundle 中所有 `.epub`
-3. `RDReaderManager.demoBookItems(in:)` 已将这些样书接入 demo 书架入口
-4. `ViewController` 支持通过 `-demo-book-title` 启动参数自动打开指定样书
-
-这意味着当前并不缺“样书载体”或“执行入口”,更缺的是:
-
-1. 哪本样书用于验证哪类能力
-2. 每次验证时要记录哪些数据
-3. 什么叫验证通过
-4. 如何把结果沉淀成可比较的基线
-
-### 当前实现存在的问题
-
-#### 问题 1:四本样书当前只是 demo 资源,不是正式基线夹具
-
-现在这四本书更多承担“可打开、可展示”的职责,但还没有被正式定义为:
-
-1. 固定基线样书
-2. 各自覆盖的风险场景
-3. 必测能力清单
-4. 异常归档与复验对象
-
-#### 问题 2:当前没有统一的基线指标结构
-
-`Doc/EPUB_MAINTENANCE.md` 已明确提到要补齐:
-
-1. 分页耗时
-2. 目录命中率
-3. 末页事件
-4. 设置恢复稳定性
-
-但当前还没有统一规定:
-
-1. 指标如何采集
-2. 指标如何记录
-3. 指标的通过标准是什么
-4. 下次如何与这次比较
-
-#### 问题 3:样书验证目标混杂了性能、正确性与兼容性
-
-“样书基线验证”至少同时包含三类验证目标:
-
-1. 性能基线:分页耗时、页数规模
-2. 正确性基线:目录跳转、末页事件、位置恢复
-3. 兼容性基线:不同 EPUB 类型在不同渲染路径下是否稳定
-
-如果不拆开,执行时很容易退化成“随便翻一下,感觉没问题”。
-
-## 需求拆解
-
-建议把“样书基线验证”拆成四部分来推进。
-
-### 1. 固定样书集
-
-先把四本样书从“demo 资源”提升为“固定基线夹具”,后续版本迭代尽量不随意替换。
-
-### 2. 固定样书职责
-
-每本样书都明确它主要覆盖哪些风险,而不是所有书都做完全相同的验证。
-
-### 3. 固定基线指标
-
-每次验证时都按同一组字段记录结果,保证可比较。
-
-### 4. 固定验证流程与输出模板
-
-每轮验证都按统一步骤执行,并把结果写入统一模板,避免结论只停留在口头描述。
-
-## 技术路线对比
-
-### 路线 A:完全手工验证
-
-思路:
-
-1. 打开四本样书
-2. 手工翻页、点目录、调字号
-3. 用主观结论判断是否正常
-
-优点:
-
-1. 上手快
-2. 不需要额外改工程
-
-风险:
-
-1. 结果不稳定
-2. 不同人执行结论可能不同
-3. 无法形成长期可比较的基线
-
-### 路线 B:一开始就全量自动化
-
-思路:
-
-1. 直接把四本样书的打开、翻页、跳转、恢复全部接入 UI 自动化或脚本采集
-2. 让基线数据自动产出
-
-优点:
-
-1. 理想状态下最规范
-2. 长期收益高
-
-风险:
-
-1. 前期成本大
-2. 当前测试基础设施还不完整
-3. 部分阅读体验问题在第一阶段更适合半自动确认
-
-### 路线 C:先建立半自动基线,再逐步自动化
-
-思路:
-
-1. 先固定样书矩阵与基线字段
-2. 借助现有 demo 自动打开入口执行统一验证流程
-3. 先沉淀结构化结果
-4. 再将适合自动化的部分逐步纳入 XCTest / XCUITest
-
-优点:
-
-1. 投入和收益平衡更好
-2. 能更快形成第一版基线
-3. 不会被早期自动化建设阻塞
-
-风险:
-
-1. 需要纪律性执行模板
-2. 初期仍有部分验证依赖人工操作
-
-### 推荐结论
-
-建议采用:
-
-**主路线:路线 C,先建立半自动样书基线,再逐步自动化**
-
-原因:
-
-1. 当前仓库已经有四本固定样书和自动打开入口
-2. 样书基线验证的第一目标是“有可比较的结果”,而不是“立刻完全自动化”
-3. 自动化测试体系尚在建设中,先立住基线矩阵更划算
-
-## 推荐方案
-
-### 方案总览
-
-样书基线验证第一阶段建议形成如下结构:
-
-1. 固定四本样书,不随意替换
-2. 每本样书绑定主要验证职责
-3. 每轮版本验证按统一流程执行
-4. 每次都输出结构化基线表
-5. 对异常项保留备注与复验结论
-
-### 四本样书矩阵
-
-> 注:以下矩阵以当前样书名称和文档信息为基础,readingProfile 可在首次正式基线验证时补齐实测结果。
-
-| 样书 | 主要定位 | 重点验证能力 | 适合记录的核心指标 |
-|------|----------|--------------|--------------------|
-| `爱忘事的熊爷爷.epub` | 轻量快速回归样书 | 打开成功、基础分页、目录基本可用、设置恢复 | 打开耗时、总页数、TOC 基本命中、设置恢复 |
-| `张学良传.epub` | 标准长文 reflowable 样书 | 长文分页、目录跳转、字号变化后位置恢复 | 首次分页耗时、TOC 命中率、字号调整后恢复 |
-| `宝山辽墓材料与释读.epub` | 复杂结构与学术内容样书 | 复杂 TOC / fragment 命中、图片与正文结构稳定性、搜索与标注抽样 | TOC 命中率、fragment 命中、搜索命中、高亮恢复 |
-| `《凡人修仙传》精校版全本.epub` | 大体量长书与压力样书 | 大体量分页稳定性、持久化恢复、末页事件、长时阅读链路 | 分页耗时、总页数、末页事件、重进恢复稳定性 |
-
-### 推荐验证维度
-
-第一阶段建议围绕下面这些维度建立固定基线:
-
-1. 打开是否成功
-2. readingProfile 类型
-3. spine 数量
-4. 总页数是否合理
-5. 首次分页耗时
-6. TOC 是否可打开
-7. TOC 抽样命中率
-8. 末页事件是否正常触发
-9. 字号调整后位置是否稳定恢复
-10. 主题 / 设置是否稳定恢复
-11. 重进后阅读位置是否恢复
-12. 标注或高亮是否恢复
-
-其中第一阶段最核心的四项,应与 `Doc/EPUB_MAINTENANCE.md` 对齐:
-
-1. 分页耗时
-2. 目录命中率
-3. 末页事件
-4. 设置恢复稳定性
-
-## 推荐执行流程
-
-建议每本样书都按同一流程执行,避免漏项。
-
-### Step B1:冷启动打开样书
-
-目标:
-
-1. 验证样书可正常进入阅读器
-2. 记录首次打开与首次分页表现
-
-记录:
-
-1. 是否打开成功
-2. readingProfile
-3. spine 数量
-4. 总页数
-5. 首次分页耗时
-
-### Step B2:目录抽样验证
-
-目标:
-
-1. 验证 TOC 是否能正常打开
-2. 验证典型目录项是否能跳转到正确章节或语义位置
-
-建议:
-
-1. 每本样书至少抽样 3 到 5 个目录项
-2. 包含开头、中间、靠后位置
-3. 如果目录层级复杂,额外抽样一个带 fragment 的条目
-
-记录:
-
-1. 抽样条目数
-2. 命中数
-3. 命中率
-4. 失败条目与现象
-
-### Step B3:末页事件验证
-
-目标:
-
-1. 验证阅读器到达末页时的状态变化是否正确
-2. 检查不会提前触发或漏触发
-
-记录:
-
-1. 是否到达末页
-2. 末页事件是否正常触发
-3. 是否出现重复触发或不触发
-
-### Step B4:设置恢复稳定性验证
-
-目标:
-
-1. 修改字号、主题或其他关键阅读设置
-2. 验证设置变更后分页是否稳定
-3. 验证退出重进后设置是否恢复
-
-记录:
-
-1. 修改的设置项
-2. 修改后是否立即生效
-3. 重进后是否恢复
-4. 是否伴随位置飘移或异常白页
-
-### Step B5:位置与标注恢复抽样验证
-
-目标:
-
-1. 在样书中间位置退出重进
-2. 验证阅读位置恢复
-3. 抽样验证一条高亮或标注恢复
-
-记录:
-
-1. 退出前位置
-2. 重进后位置
-3. 是否在同一章节或邻近语义位置
-4. 高亮 / 标注是否仍存在
-
-## 基线表模板
-
-建议每轮验证都至少产出两张表:
-
-1. 总览表
-2. 单书详情表
-
-### 模板 1:样书基线总览表
-
-| 日期 | 版本 / 分支 | 样书 | readingProfile | spine 数量 | 总页数 | 首次分页耗时 | TOC 抽样 / 命中 | 末页事件 | 设置恢复 | 位置恢复 | 标注恢复 | 结论 | 备注 |
-|------|--------------|------|----------------|------------|--------|--------------|-----------------|----------|----------|----------|----------|------|------|
-| YYYY-MM-DD | branch / commit | 爱忘事的熊爷爷 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 通过 / 风险 / 失败 | 待填 |
-| YYYY-MM-DD | branch / commit | 张学良传 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 通过 / 风险 / 失败 | 待填 |
-| YYYY-MM-DD | branch / commit | 宝山辽墓材料与释读 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 通过 / 风险 / 失败 | 待填 |
-| YYYY-MM-DD | branch / commit | 《凡人修仙传》精校版全本 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 通过 / 风险 / 失败 | 待填 |
-
-### 模板 2:单书详细验证表
-
-#### 样书信息
-
-| 字段 | 值 |
-|------|----|
-| 样书名称 | 待填 |
-| 文件大小 | 待填 |
-| readingProfile | 待填 |
-| spine 数量 | 待填 |
-| TOC 条目数 | 待填 |
-| 总页数 | 待填 |
-| 验证日期 | 待填 |
-| 验证版本 / 分支 | 待填 |
-
-#### 核心基线结果
-
-| 项目 | 结果 | 备注 |
-|------|------|------|
-| 打开成功 | 通过 / 失败 | 待填 |
-| 首次分页耗时 | 待填 | 单位建议统一为 ms 或 s |
-| TOC 打开 | 通过 / 失败 | 待填 |
-| TOC 抽样命中率 | 待填 | 例如 `4/5` |
-| 末页事件 | 通过 / 风险 / 失败 | 待填 |
-| 字号调整后恢复 | 通过 / 风险 / 失败 | 待填 |
-| 主题 / 设置恢复 | 通过 / 风险 / 失败 | 待填 |
-| 重进后位置恢复 | 通过 / 风险 / 失败 | 待填 |
-| 高亮 / 标注恢复 | 通过 / 风险 / 失败 | 待填 |
-
-#### TOC 抽样记录
-
-| 序号 | TOC 条目 | 预期目标 | 实际结果 | 是否命中 | 备注 |
-|------|----------|----------|----------|----------|------|
-| 1 | 待填 | 待填 | 待填 | 是 / 否 | 待填 |
-| 2 | 待填 | 待填 | 待填 | 是 / 否 | 待填 |
-| 3 | 待填 | 待填 | 待填 | 是 / 否 | 待填 |
-| 4 | 待填 | 待填 | 待填 | 是 / 否 | 待填 |
-| 5 | 待填 | 待填 | 待填 | 是 / 否 | 待填 |
-
-#### 异常与复验记录
-
-| 异常编号 | 场景 | 现象 | 复现条件 | 当前结论 | 备注 |
-|----------|------|------|----------|----------|------|
-| B-001 | 待填 | 待填 | 待填 | 待确认 / 已复现 / 已修复 | 待填 |
-
-## 推荐输出规范
-
-为了让基线真正可比较,建议每轮验证输出时统一遵守以下规范:
-
-### 1. 样书集固定
-
-第一阶段尽量只使用当前这四本 EPUB,不随意更换,避免基线漂移。
-
-### 2. 字段命名固定
-
-例如:
-
-1. `首次分页耗时`
-2. `TOC 抽样命中率`
-3. `末页事件`
-4. `设置恢复`
-5. `位置恢复`
-
-尽量不要每次换表头名称。
-
-### 3. 结论分级固定
-
-建议统一使用:
-
-1. `通过`
-2. `风险`
-3. `失败`
-
-其中:
-
-1. `通过`:功能符合预期,无明显异常
-2. `风险`:功能基本可用,但存在偏差、偶发不稳或需继续观察
-3. `失败`:功能不符合预期,影响可用性
-
-### 4. 差异说明固定
-
-如果与上次基线相比发生变化,建议在备注中明确:
-
-1. 是否变快
-2. 是否变慢
-3. 是否命中率下降
-4. 是否新增异常
-5. 是否已有异常被修复
-
-## 风险与注意点
-
-### 风险 1:把基线验证做成纯主观体验记录
-
-如果只有“感觉正常”“翻起来没问题”这类结论,后续几乎无法比较。
-
-### 风险 2:过度依赖绝对页码
-
-页码会受到字号、行距、视口与渲染策略影响,样书基线更适合比较:
-
-1. 是否能正常分页
-2. 是否恢复到同章节或邻近语义位置
-3. 是否出现明显回归
-
-而不是执着于固定页号完全一致。
-
-### 风险 3:样书职责不清导致验证重复或漏项
-
-如果四本书都做完全相同的验证,会浪费时间;如果每本书都“随便点几下”,又会漏掉高风险场景。
-
-## 推荐实施顺序
-
-如果目标是“最小投入换最大稳定性提升”,建议按下面顺序推进:
-
-1. 先将四本样书正式定义为固定基线样书
-2. 先用本文件中的矩阵与模板跑第一轮人工基线
-3. 在第一轮基线中补齐每本样书的实测 `readingProfile`、spine 数量、总页数等信息
-4. 统一记录异常项与复验结论
-5. 再将其中适合自动化的部分逐步并入 `XCTest / XCUITest`
-
-## 推荐结论
-
-“样书基线验证”这项需求,建议最终收敛为下面这句话:
-
-**基于当前 demo 内置的四本固定 EPUB 样书,建立一套统一矩阵、统一流程、统一模板的基线验证机制,首期重点覆盖分页耗时、目录命中率、末页事件和设置恢复稳定性,并为后续自动化回归提供稳定样书夹具。**
diff --git a/Doc/FeatureSolution/横竖屏切换支持方案讨论.md b/Doc/FeatureSolution/横竖屏切换支持方案讨论.md
deleted file mode 100644
index d3ceade..0000000
--- a/Doc/FeatureSolution/横竖屏切换支持方案讨论.md
+++ /dev/null
@@ -1,372 +0,0 @@
-# 横竖屏切换支持方案讨论
-
-## 需求背景
-
-`Doc/EPUB_MAINTENANCE.md` 与 `Doc/ARCHITECTURE.md` 都把“横竖屏切换支持”列为当前阅读器的高优先级待补能力。
-
-目标不是单纯“屏幕旋转后不崩”,而是让 EPUB 阅读器在横竖屏切换后具备以下稳定行为:
-
-1. 正文重新按新视口尺寸分页或重排
-2. 阅读位置尽量准确恢复,不明显跳章、跳页
-3. 工具栏、目录、标注、搜索状态不异常
-4. pageCurl / horizontalScroll / verticalScroll / horizontalCoverScroll 四种展示模式行为一致
-5. WebView、DTCoreText、fixed layout 三条正文路径都能正确处理
-
-## 代码事实
-
-### 已有基础
-
-当前仓库并不是完全没有横竖屏处理基础:
-
-- `RDReaderView.layoutSubviews()` 会检测 `previousIsLandscape` 与 `isLandscape` 的变化
- 文件:`Sources/RDReaderView/RDReaderView.swift`
-- `RDReaderView.orientationChanged(isNowLandscape:)` 已能:
- - 通知 `readerViewOrientationWillChange`
- - 更新 `layout.isLandscapeDualPage`
- - `pageCurl` 模式下重建 `UIPageViewController`
- - 滚动模式下 `invalidateLayout + reloadData + setContentOffset`
-- `RDEPUBReaderController.readerViewOrientationWillChange(...)` 当前会直接调用 `repaginatePreservingCurrentLocation()`
-- `RDEPUBReaderController.repaginatePreservingCurrentLocation()` 已具备“先记录当前位置,再重新分页,再恢复位置”的主链路
-- `RDEPUBLocation` 已采用 `href + progression + lastProgression + fragment` 模型,本身适合应对字号变化和尺寸变化后的重定位
-
-结论:
-
-- 基础设施已经存在
-- 真正缺的是“时机是否稳定、入口是否统一、三条正文路径是否都覆盖、是否会重复触发”
-
-### 当前实现存在的问题
-
-#### 问题 1:方向变化回调被 `landscapeDualPageEnabled` 绑定
-
-`RDReaderView.layoutSubviews()` 目前有:
-
-```swift
-guard landscapeDualPageEnabled, bounds.width > 0, bounds.height > 0 else { return }
-```
-
-这意味着:
-
-- 只有开启 `landscapeDualPageEnabled` 时才会进入方向变化检测
-- 如果宿主关闭横屏双页,但正文仍然会因为宽高变化而需要重排,此时不会触发回调
-
-这对以下场景不正确:
-
-1. `textReflowable` 文本书籍在横竖屏切换后,行宽必然变化,应重新分页
-2. `webInteractive` 可重排正文在横竖屏切换后,列分页宽度变化,应重新分页
-3. fixed layout 在 spread 模式下也可能因视口变化而需要重新生成页面模型
-
-#### 问题 2:重新分页触发时机偏早
-
-当前 `readerViewOrientationWillChange` 是从 `RDReaderView.layoutSubviews()` 中异步抛出。
-
-这有几个风险:
-
-1. 此时上层 VC 的 `view.bounds`、safe area、sheet 布局动画可能还在变化中
-2. `paginatePublication()` 如果过早读取 `currentLayoutContext().viewportSize`,可能拿到中间态尺寸
-3. `RDReaderView` 自己已经在同一轮变化里做了 `reloadData` / `transitionToPage`,而 `RDEPUBReaderController` 又会发起新一轮分页,容易出现双重刷新
-
-#### 问题 3:职责边界不够清晰
-
-目前横竖屏变化同时由两层在做事:
-
-- `RDReaderView`:处理双页布局、pageCurl 容器重建、滚动模式 offset 恢复
-- `RDEPUBReaderController`:处理 EPUB 重新分页
-
-但两层之间没有一个明确的“统一入口”来协调:
-
-1. 是否真的需要重分页
-2. 什么时候用最终尺寸重分页
-3. 何时忽略重复触发
-4. 何时只刷新布局,不重做完整分页
-
-#### 问题 4:文档状态与代码状态不一致
-
-文档把横竖屏切换标为“未实现”,但代码里已经有半套逻辑。
-
-这说明当前更准确的描述应当是:
-
-- “已有局部实现”
-- “尚未形成稳定、完整、可验收的横竖屏支持闭环”
-
-## 需求拆解
-
-为了让“横竖屏切换支持”真正完成,建议把需求拆成四部分:
-
-### 1. 稳定感知视口变化
-
-不仅要感知“横屏/竖屏布尔值变化”,还要感知:
-
-- 宽高尺寸变化
-- safe area 变化
-- iPad 分屏、多窗口、sheet 尺寸变化
-
-因此,真正要监听的不是“orientation”,而是“阅读视口发生了足以影响分页的变化”。
-
-### 2. 统一触发重新分页
-
-所有需要重排正文的场景都应走统一入口,例如:
-
-```swift
-handleViewportChange(reason: .orientationTransition)
-```
-
-由它统一负责:
-
-1. 读取当前位置
-2. 比较旧 viewport 与新 viewport
-3. 防抖与去重
-4. 发起分页
-5. 恢复定位
-
-### 3. 区分“容器布局刷新”和“正文重分页”
-
-不是所有变化都必须触发完整分页,但横竖屏切换通常都需要:
-
-- `textReflowable`:重建 `RDEPUBTextBook`
-- `webInteractive`:重跑 `RDEPUBPaginator`
-- `webFixedLayout`:重建 fixed spread snapshot
-
-而 `RDReaderView` 自己的 pageCurl 双页容器重建仍可保留,但不应和正文分页时机互相打架。
-
-### 4. 建立回归验收矩阵
-
-横竖屏切换是高联动场景,至少要验证:
-
-1. 无选区、无工具栏时切换
-2. 有目录面板、设置面板、标注列表时切换
-3. 搜索命中页、标注页、末页时切换
-4. `landscapeDualPageEnabled = true / false`
-5. pageCurl / scroll 系列模式
-
-## 技术路线对比
-
-### 路线 A:继续依赖 `RDReaderView.readerViewOrientationWillChange`
-
-思路:
-
-- 保留现有 `layoutSubviews() -> orientationChanged -> delegate`
-- 在 `RDEPUBReaderController` 内增强去重、防抖和最终尺寸判断
-
-优点:
-
-1. 改动范围小
-2. 能延续现有 `RDReaderView` 双页逻辑
-
-风险:
-
-1. 触发时机仍然偏依赖 `layoutSubviews()`
-2. 仍容易与 VC 生命周期中的尺寸变化打架
-3. 继续把“正文分页”绑定到“容器方向回调”,抽象层次不够稳定
-
-### 路线 B:由 `RDEPUBReaderController.viewWillTransition(to:with:)` 统一接管
-
-思路:
-
-1. 在 `RDEPUBReaderController` 实现 `viewWillTransition(to:with:)`
-2. 利用 `transitionCoordinator` 等待旋转动画接近完成
-3. 在 completion 中读取最终 `viewportSize`
-4. 统一调用 `handleViewportChange(reason:)`
-5. `RDReaderView.readerViewOrientationWillChange` 只保留容器级双页重建职责,不再直接触发正文分页
-
-优点:
-
-1. 分页时机更接近最终尺寸
-2. 更符合 UIKit 对旋转和尺寸变化的生命周期
-3. 更容易做重复触发抑制
-4. 更适合以后扩展到 iPad 分屏、多窗口
-
-风险:
-
-1. 需要重新梳理 `RDReaderView` 与 `RDEPUBReaderController` 的职责分界
-2. 需要验证 pageCurl 双页重建与正文分页之间的顺序
-
-### 推荐结论
-
-建议采用:
-
-**主路线:路线 B,由 `RDEPUBReaderController.viewWillTransition(to:with:)` 统一接管正文重分页**
-**保留 `RDReaderView` 现有方向变化逻辑,但将其职责收敛为容器布局刷新和双页模式内部处理**
-
-原因:
-
-1. 需求本质是“视口变化后的正文重排”,最合理的宿主是控制器,而不是容器内部的 `layoutSubviews()`
-2. 现有 `RDReaderView` 回调机制已经能服务内部双页布局,但不适合作为 EPUB 正文分页的唯一入口
-3. `viewWillTransition` + `transitionCoordinator` 更容易拿到稳定尺寸,并减少重复分页
-
-## 推荐方案
-
-### 方案总览
-
-引入一条统一的横竖屏处理主链路:
-
-```swift
-viewWillTransition(to:with:)
- -> scheduleViewportTransition(reason: .orientation)
- -> handleViewportChangeIfNeeded(finalViewportSize)
- -> repaginatePreservingCurrentLocation()
- -> finishPagination(restoreLocation:)
-```
-
-同时把 `RDReaderView` 内部职责收敛为:
-
-1. 更新 `layout.isLandscapeDualPage`
-2. pageCurl 模式下重建 `UIPageViewController`
-3. 滚动模式刷新 collectionView 布局
-
-不再让 EPUBUI 直接在 `readerViewOrientationWillChange` 中立即重新分页。
-
-### 关键改动建议
-
-#### Task O1:增加控制器级旋转入口
-
-文件:
-
-- `Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`
-
-改动:
-
-- 实现 `viewWillTransition(to:with:)`
-- 在 `transitionCoordinator` completion 中比较新旧 viewport
-- 统一调 `handleViewportChange(reason:)`
-
-完成定义:
-
-- 旋转后分页使用最终尺寸,而不是中间态尺寸
-
-#### Task O2:引入 viewport 去重与防抖
-
-文件:
-
-- `Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`
-
-改动:
-
-- 保存最近一次已应用的 `viewportSignature`
-- 仅当宽高变化超过阈值时才触发重新分页
-- 防止 `viewDidLayoutSubviews`、`orientation callback`、`viewWillTransition` 连续多次重复触发
-
-推荐结构:
-
-```swift
-private struct RDEPUBViewportSignature: Equatable {
- let width: CGFloat
- let height: CGFloat
- let safeTop: CGFloat
- let safeBottom: CGFloat
-}
-```
-
-完成定义:
-
-- 一次横竖屏切换只触发一次有效正文重分页
-
-#### Task O3:调整 `readerViewOrientationWillChange` 职责
-
-文件:
-
-- `Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`
-- 如有必要 `Sources/RDReaderView/RDReaderView.swift`
-
-改动:
-
-- `RDEPUBReaderController.readerViewOrientationWillChange` 不再直接 `repaginatePreservingCurrentLocation()`
-- 保留它作为兼容钩子,或仅做轻量状态标记
-
-完成定义:
-
-- 容器内部布局刷新与正文分页不再相互打架
-
-#### Task O4:放宽 `RDReaderView` 的方向检测前置条件
-
-文件:
-
-- `Sources/RDReaderView/RDReaderView.swift`
-
-改动:
-
-- 去掉 `layoutSubviews()` 中对 `landscapeDualPageEnabled` 的硬性 guard
-- 方向变化检测应独立于“是否启用横屏双页”
-
-完成定义:
-
-- 即使 `landscapeDualPageEnabled == false`,视口变化仍可被上层感知并触发重分页
-
-注意事项:
-
-- 双页布局本身仍然只在 `landscapeDualPageEnabled == true` 时开启
-- 但“是否需要感知尺寸变化”不能绑定到这个开关
-
-#### Task O5:验证三条正文路径的恢复策略
-
-文件:
-
-- `Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`
-- `Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift`
-- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift`
-
-改动:
-
-- 验证 `restoreReadingLocation()` 在以下路径都稳定:
- - `textReflowable`
- - `webInteractive`
- - `webFixedLayout`
-
-完成定义:
-
-- 横竖屏切换后,恢复位置不明显跳页、跳章
-
-#### Task O6:补文档与验收矩阵
-
-文件:
-
-- `Doc/EPUB_MAINTENANCE.md`
-- `Doc/ARCHITECTURE.md`
-- 如有必要 `Doc/EPUBUI_功能实现逻辑.md`
-
-改动:
-
-- 将“横竖屏切换未做”更新为更准确的实现状态
-- 补充最终采用的触发时机与回归清单
-
-完成定义:
-
-- 文档与代码状态一致
-
-## 风险点
-
-1. **pageCurl 双页重建顺序**
- `RDReaderView` 自己会重建 `UIPageViewController`,如果 `RDEPUBReaderController` 同时触发分页,需要验证两者先后顺序
-2. **重复分页**
- 旋转时 UIKit 常伴随多次 layout / safe area 变化,如果没有 `viewportSignature`,很容易多次重排
-3. **iPad 场景复杂度**
- 分屏、多窗口、Stage Manager 下不一定发生“方向变化”,但一定发生“视口变化”,所以方案必须以 viewport 为核心
-4. **fixed layout spread 恢复**
- 横屏双页和竖屏单页之间切换时,恢复到哪一页的左页 / 右页,需要以 `RDEPUBLocation` 为准而不是旧页号
-
-## 推荐实施顺序
-
-1. 先做 `O1 + O2`,把控制器级 viewport 变化入口和防抖建起来
-2. 再做 `O3 + O4`,把旧的 delegate 触发职责收窄
-3. 然后验证 `O5`,重点跑三条正文路径和四种展示模式
-4. 最后做 `O6`,同步文档与验收矩阵
-
-## 验收标准
-
-1. 横竖屏切换后,`textReflowable` 路径会按新尺寸重新分页,并恢复到接近原阅读位置
-2. 横竖屏切换后,`webInteractive` 路径会重新分页,并恢复到接近原阅读位置
-3. 横竖屏切换后,`webFixedLayout` 路径会按新 spread 规则重建页面模型,并恢复到正确资源
-4. `landscapeDualPageEnabled == false` 时,横竖屏切换仍然会触发必要的重排
-5. 一次旋转只触发一次有效正文重分页,不出现连续多次闪烁刷新
-6. pageCurl / horizontalScroll / verticalScroll / horizontalCoverScroll 四种模式下都不出现明显错页、白页或工具栏错位
-
-## 结论
-
-横竖屏切换支持不是“从零开始新增一套能力”,而是要把当前分散在 `RDReaderView` 和 `RDEPUBReaderController` 里的半套逻辑收敛成一个稳定闭环。
-
-最推荐的方向是:
-
-- `RDEPUBReaderController` 用 `viewWillTransition(to:with:)` 统一接管正文级重分页
-- `RDReaderView` 保留容器级双页布局和 pageCurl 重建职责
-- 以 `viewport` 变化而不是 `isLandscape` 布尔值作为真正的分页触发依据
-
-这样既能复用现有代码,又能把“文档标注为未完成”的横竖屏需求收敛成可实现、可回归、可交付的一阶段方案。
diff --git a/Doc/FeatureSolution/自动化测试方案讨论.md b/Doc/FeatureSolution/自动化测试方案讨论.md
deleted file mode 100644
index a1302b1..0000000
--- a/Doc/FeatureSolution/自动化测试方案讨论.md
+++ /dev/null
@@ -1,418 +0,0 @@
-# 自动化测试方案讨论
-
-## 需求背景
-
-`Doc/EPUB_MAINTENANCE.md` 已将“自动化测试”列为 EPUB 阅读器下一阶段的高优先级能力之一。
-
-当前阅读器已经具备较完整的主链路能力,包括:
-
-1. EPUB 解析与打开
-2. WebView / DTCoreText / fixed-layout 三条正文渲染路径
-3. 目录跳转、阅读位置恢复、主题与字号调整
-4. 高亮、划线、批注、搜索等阅读交互能力
-
-但这些能力目前主要依赖手工验证,缺少自动化回归保护。随着横竖屏切换、书签、更多搜索体验和阅读增强能力继续叠加,单靠人工回归会越来越难以稳定覆盖高联动场景。
-
-因此,“自动化测试”这项需求的目标不是单纯增加几条测试用例,而是为 EPUB 阅读器建立一套可持续演进的自动化回归体系,优先保护以下核心链路:
-
-1. 解析
-2. 分页
-3. 定位恢复
-4. 目录跳转
-5. 标注恢复
-
-## 代码事实
-
-### 已有基础
-
-当前仓库并不是完全没有可测试的基础:
-
-1. `Sources/RDReaderView/EPUBCore/` 已经把 parser、resourceResolver、location、readingSession、paginator 等能力拆成了相对独立的模块
-2. `Sources/RDReaderView/EPUBTextRendering/` 已经将 DTCoreText 渲染、fragment 提取、富文本分页等逻辑收敛到明确的工具类型中
-3. `RDEPUBLocation`、`RDEPUBHighlight`、TOC / spine / manifest 等模型本身适合做 deterministic 的断言
-4. `ReadViewSDKDemo` 已经提供真实运行容器,后续可以承载 UI 自动化测试
-
-结论:
-
-1. 核心逻辑并非不可测
-2. 更缺的是测试 target、测试夹具、断言策略和回归矩阵
-
-### 当前实现存在的问题
-
-#### 问题 1:工程里还没有测试承载层
-
-从 `ReadViewSDKDemo.xcodeproj` 当前 target 配置来看,只有 `ReadViewSDKDemo` app target,没有独立的 `Tests` 或 `UITests` target。
-
-这意味着当前并没有:
-
-1. 用于跑 XCTest 的基础 target
-2. 用于跑 XCUITest 的 UI 测试 target
-3. 稳定的测试资源加载方式
-
-#### 问题 2:关键链路缺少自动回归保护
-
-当前高风险能力包括:
-
-1. `RDEPUBParser` 解析 EPUB ZIP / OPF / spine / TOC
-2. `RDEPUBResourceResolver.normalizedHref(_:)` 的路径标准化
-3. `RDEPUBPaginator` 与 `RDEPUBTextPaginationSupport` 的分页结果
-4. `RDEPUBReaderController.restoreLocation(_:)` 的定位恢复
-5. 高亮 / 批注 persistence 的恢复链路
-
-这些地方一旦回归,通常会表现为:
-
-1. 打不开书
-2. 目录跳错
-3. 字号变化后位置飘移
-4. 标注丢失或恢复失败
-
-而这些问题都很适合被自动化测试尽早捕获。
-
-#### 问题 3:如果直接从 UI 自动化起步,成本和脆弱性都偏高
-
-EPUB 阅读器的很多问题发生在 UI 之下,例如:
-
-1. TOC href 标准化错误
-2. `fragment -> offset` 映射偏移
-3. 分页结果为空或页数异常
-4. `href + progression` 回落恢复逻辑不稳定
-
-如果这些问题都等到 `XCUITest` 才暴露:
-
-1. 定位根因会很慢
-2. 测试执行时间会更长
-3. 异步加载、动画、WebView 时序会让用例更脆
-
-#### 问题 4:当前还没有固定的样书基线与测试夹具约定
-
-要让自动化测试长期稳定,必须明确:
-
-1. 哪几本样书是测试夹具
-2. 每本样书用于验证哪种能力
-3. 哪些断言可以依赖“页号”,哪些只能依赖 `href / fragment / progression`
-
-如果没有这层约束,测试很容易随着样书变化或样式调整而频繁漂移。
-
-## 需求拆解
-
-建议把“自动化测试”需求拆成四部分,而不是把所有验证都堆到同一层。
-
-### 1. 建立测试基础设施
-
-先补齐可执行自动化测试的工程结构,包括:
-
-1. `ReadViewSDKDemoTests`
-2. `ReadViewSDKDemoUITests`
-3. 样书 fixtures 目录
-4. 统一的测试资源读取辅助工具
-
-这是后续一切测试工作的前提。
-
-### 2. 建立逻辑层 XCTest
-
-这部分优先覆盖稳定、纯逻辑、可快速执行的能力:
-
-1. parser 解析
-2. href 标准化
-3. TOC 路径映射
-4. location / progression / fragment 相关转换
-5. persistence 编解码
-
-目标是让每次改动 parser、模型、定位和持久化逻辑时,都有快速反馈。
-
-### 3. 建立集成层 XCTest
-
-这部分不直接走 UI 自动化,但会驱动真实样书、真实分页和真实恢复链路,重点覆盖:
-
-1. reflowable 样书分页
-2. fixed-layout 样书页面模型生成
-3. 字号变化后位置恢复
-4. 目录跳转命中
-5. 标注恢复
-
-目标是把“阅读核心链路”在逻辑层和 UI 层之间补上一层更贴近真实运行的保护网。
-
-### 4. 建立少量高价值 XCUITest
-
-UI 自动化不追求全覆盖,而是做真实用户闭环的冒烟回归,例如:
-
-1. 打开样书进入阅读器
-2. 目录跳转
-3. 调整字号
-4. 创建高亮并重新进入验证恢复
-5. 横竖屏切换后继续阅读
-
-目标是证明“用户真的能完成关键操作”,而不是把所有细节都放进 UI 测试里。
-
-## 技术路线对比
-
-### 路线 A:优先建设 XCUITest
-
-思路:
-
-1. 先搭 UI 自动化
-2. 用点击、滑动、旋转和断言可见文本的方式覆盖主要能力
-
-优点:
-
-1. 结果直观,容易贴近真实用户行为
-2. 可以较快形成“端到端可用”的感知
-
-风险:
-
-1. 调试成本高
-2. 受动画、异步、WebView 时序影响大
-3. 很难快速判断问题出在 parser、分页还是 UI
-4. 执行时间更长,不适合作为最早期主回归层
-
-### 路线 B:优先建设 XCTest
-
-思路:
-
-1. 先为逻辑与集成层补单元测试
-2. 把解析、分页、定位恢复等能力尽量在非 UI 层验证
-
-优点:
-
-1. 稳定性更高
-2. 运行更快
-3. 更利于问题定位
-4. 更适合作为日常改动的主回归层
-
-风险:
-
-1. 不能完全证明 UI 交互链路没问题
-2. 覆盖不到真实点击、WebView 手势和界面状态同步问题
-
-### 路线 C:分层推进
-
-思路:
-
-1. 先建立测试基础设施
-2. 以 XCTest 为主搭建核心保护网
-3. 再补少量 XCUITest 做端到端闭环
-
-优点:
-
-1. 投入与收益平衡更好
-2. 能优先保护最容易回归的核心能力
-3. 既兼顾稳定性,也兼顾真实用户路径
-
-风险:
-
-1. 初期需要先设计测试层次和夹具约定
-2. 需要控制 UI 测试范围,避免后续无节制膨胀
-
-### 推荐结论
-
-建议采用:
-
-**主路线:路线 C,分层推进,XCTest 为主,XCUITest 为辅**
-
-原因:
-
-1. 当前最需要保护的是解析、分页、定位恢复这些高风险核心链路
-2. 仓库当前还没有测试 target,先从更稳定的 XCTest 起步更合适
-3. 少量 XCUITest 足以证明关键阅读流程可用,不必一开始追求全面 UI 覆盖
-
-## 推荐方案
-
-### 方案总览
-
-自动化测试第一阶段建议形成如下结构:
-
-```text
-ReadViewSDKDemoTests
- - ParserTests
- - ResourceResolverTests
- - LocationRestoreTests
- - TextPaginationTests
- - HighlightPersistenceTests
-
-ReadViewSDKDemoUITests
- - ReaderSmokeTests
- - ReaderNavigationTests
- - ReaderAnnotationTests
- - ReaderRotationTests
-```
-
-同时建立一套最小样书夹具集:
-
-1. `reflowable-basic.epub`
-2. `fixed-layout-basic.epub`
-3. `image-heavy.epub`
-4. `toc-fragment.epub`
-
-每本样书都应明确“它用来验证什么”,而不是仅作为示例文件存在。
-
-### 关键改动建议
-
-#### Task T1:建立测试 target 与基础资源加载能力
-
-文件范围:
-
-1. `ReadViewSDKDemo/ReadViewSDKDemo.xcodeproj`
-2. 新增 `ReadViewSDKDemoTests/`
-3. 新增 `ReadViewSDKDemoUITests/`
-4. 新增测试夹具目录
-
-改动:
-
-1. 增加 unit test target
-2. 增加 UI test target
-3. 为测试 target 挂载样书与辅助资源
-4. 提供统一 `TestBookLoader` / `FixtureLocator` 之类的测试辅助工具
-
-完成定义:
-
-1. 工程可以直接运行测试
-2. 测试代码可以稳定读取样书资源
-
-#### Task T2:补第一批逻辑层 XCTest
-
-建议首批覆盖:
-
-1. `RDEPUBParser` 能解析有效样书并产出非空 spine
-2. `RDEPUBResourceResolver.normalizedHref(_:)` 能正确处理相对路径、父级路径和 fragment
-3. TOC href 能统一映射到与 spine 一致的标准化路径
-4. `RDEPUBHighlight` persistence 编解码结果一致
-5. 定位模型在常见输入下不会丢 fragment 或 progression
-
-完成定义:
-
-1. parser / resolver / persistence / location 改动有自动回归保护
-
-#### Task T3:补第一批集成层 XCTest
-
-建议首批覆盖:
-
-1. reflowable 样书分页结果非空
-2. fixed-layout 样书能建立页模型
-3. 字号变化后仍能按 `href + progression` 恢复到原章节附近
-4. 目录跳转可命中预期 spine 资源
-5. 高亮恢复后仍能定位到对应资源
-
-完成定义:
-
-1. 核心阅读链路具备非 UI 层的真实运行回归能力
-
-#### Task T4:补第一批冒烟型 XCUITest
-
-建议首批覆盖:
-
-1. 打开样书并进入阅读器
-2. 打开目录并跳转章节
-3. 调整字号后继续阅读
-4. 创建一条高亮并重进验证恢复
-5. 横竖屏切换后继续阅读
-
-完成定义:
-
-1. 至少有一条真实用户闭环可以证明主功能未损坏
-
-## 断言策略建议
-
-为了降低测试漂移,建议统一以下断言原则:
-
-### 1. 少依赖固定页号
-
-页号非常容易受以下因素影响:
-
-1. 字号
-2. 行距
-3. 视口尺寸
-4. 图片加载时序
-
-因此,自动化测试不应过度依赖“必须是第 N 页”这种断言。
-
-### 2. 优先依赖稳定语义锚点
-
-更推荐的断言对象包括:
-
-1. `href`
-2. `fragment`
-3. `progression` 所在区间
-4. spine index
-5. 高亮所属资源
-
-### 3. UI 测试只验证关键闭环
-
-XCUITest 中更适合验证:
-
-1. 页面是否成功进入
-2. 关键按钮是否可操作
-3. 操作后阅读器状态是否变化
-4. 重进后状态是否可恢复
-
-而不适合在 UI 测试中承载大量底层分页细节断言。
-
-## 验收矩阵建议
-
-自动化测试第一阶段至少要覆盖以下维度:
-
-1. `webInteractive`
-2. `webFixedLayout`
-3. `textReflowable`
-4. TOC 跳转
-5. 字号变化后恢复
-6. 高亮恢复
-7. 横竖屏切换
-8. 一到两本问题样书回归
-
-如果资源有限,建议先保证“三条渲染路径 + 两条恢复链路”:
-
-1. 三条渲染路径:`webInteractive` / `webFixedLayout` / `textReflowable`
-2. 两条恢复链路:阅读位置恢复 / 标注恢复
-
-## 风险与注意点
-
-### 风险 1:测试一开始就绑定大量脆弱 UI 细节
-
-如果测试过度依赖:
-
-1. 动画时长
-2. 可见文案位置
-3. 某个具体页号
-4. WebView 内即时渲染完成时机
-
-后续维护成本会非常高。
-
-### 风险 2:测试夹具过多但目标不清晰
-
-样书数量不是越多越好。更重要的是:
-
-1. 每本样书验证哪类能力
-2. 哪些样书是主回归集
-3. 哪些样书只在专项验证时使用
-
-### 风险 3:横竖屏与字号变化断言过于绝对
-
-这两类场景更适合验证:
-
-1. 是否仍在同一章节或邻近语义位置
-2. 是否仍保留原始 `href / fragment`
-3. progression 是否在合理偏差范围内
-
-而不适合验证“必须恢复到完全相同页号”。
-
-## 推荐实施顺序
-
-如果目标是“最小投入换最大稳定性提升”,建议按以下顺序推进:
-
-1. 建立 `Tests` / `UITests` target
-2. 补 parser / resolver / location / persistence 的 XCTest
-3. 补分页、目录跳转、定位恢复的集成测试
-4. 补 3 到 5 条高价值 XCUITest
-5. 将横竖屏、问题样书和新增功能逐步并入回归矩阵
-
-## 推荐结论
-
-“自动化测试”这项需求,建议最终收敛为下面这句话:
-
-**为 EPUB 阅读器建立一套分层自动化测试体系,以 XCTest 保护解析、分页、定位恢复、目录跳转和标注恢复等核心链路,再以少量 XCUITest 验证真实阅读闭环。**
-
-这样做的好处是:
-
-1. 能尽快为最容易回归的核心能力建立保护网
-2. 不会过早陷入脆弱的全量 UI 自动化
-3. 能为横竖屏、书签、搜索和后续阅读增强功能提供稳定回归基础
diff --git a/Podfile b/Podfile
index 80649eb..c3c3aa2 100644
--- a/Podfile
+++ b/Podfile
@@ -1,4 +1,4 @@
-platform :ios, '15.0'
+platform :ios, '15.6'
use_frameworks!
workspace 'ReadViewSDK.xcworkspace'
@@ -9,9 +9,22 @@ target 'ReadViewSDK' do
end
post_install do |installer|
- installer.pods_project.targets.each do |target|
- target.build_configurations.each do |config|
- config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO'
- end
+ apply_settings = lambda do |config|
+ config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO'
+ config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.6'
+ end
+
+ # Pods project (Pods.xcodeproj)
+ installer.pods_project.build_configurations.each { |c| apply_settings.call(c) }
+ installer.pods_project.targets.each { |t| t.build_configurations.each { |c| apply_settings.call(c) } }
+
+ installer.aggregate_targets.each do |at|
+ p = at.user_project
+ next unless p
+
+ # User project(s) that integrate Pods (e.g. ReadViewDemo / ReadViewSDK)
+ p.build_configurations.each { |c| apply_settings.call(c) }
+ p.targets.each { |t| t.build_configurations.each { |c| apply_settings.call(c) } }
+ p.save
end
end
diff --git a/ReadViewDemo/Podfile b/ReadViewDemo/Podfile
index f9dffa9..72ef2c7 100644
--- a/ReadViewDemo/Podfile
+++ b/ReadViewDemo/Podfile
@@ -1,5 +1,4 @@
-# Uncomment the next line to define a global platform for your project
-# platform :ios, '9.0'
+platform :ios, '15.6'
target 'ReadViewDemo' do
# Comment the next line if you don't want to use dynamic frameworks
@@ -9,9 +8,22 @@ target 'ReadViewDemo' do
end
post_install do |installer|
- installer.pods_project.targets.each do |target|
- target.build_configurations.each do |config|
- config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO'
- end
+ apply_settings = lambda do |config|
+ config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO'
+ config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.6'
+ end
+
+ # Pods project (Pods.xcodeproj)
+ installer.pods_project.build_configurations.each { |c| apply_settings.call(c) }
+ installer.pods_project.targets.each { |t| t.build_configurations.each { |c| apply_settings.call(c) } }
+
+ # User project(s) that integrate Pods (e.g. ReadViewDemo)
+ installer.aggregate_targets.each do |at|
+ p = at.user_project
+ next unless p
+
+ p.build_configurations.each { |c| apply_settings.call(c) }
+ p.targets.each { |t| t.build_configurations.each { |c| apply_settings.call(c) } }
+ p.save
end
end
diff --git a/ReadViewDemo/Podfile.lock b/ReadViewDemo/Podfile.lock
index 617fe25..b274494 100644
--- a/ReadViewDemo/Podfile.lock
+++ b/ReadViewDemo/Podfile.lock
@@ -48,6 +48,6 @@ SPEC CHECKSUMS:
SSAlertSwift: aad8dc0c20b36fcffe700b81d7be89d60c7ba4f1
ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351
-PODFILE CHECKSUM: c9ace43f8ce76d66d3a7c91dea869911207c0354
+PODFILE CHECKSUM: 775f5c8c488024e24d494aad6331e9fef8f9e2e5
COCOAPODS: 1.16.2
diff --git a/ReadViewDemo/Pods/Manifest.lock b/ReadViewDemo/Pods/Manifest.lock
index 617fe25..b274494 100644
--- a/ReadViewDemo/Pods/Manifest.lock
+++ b/ReadViewDemo/Pods/Manifest.lock
@@ -48,6 +48,6 @@ SPEC CHECKSUMS:
SSAlertSwift: aad8dc0c20b36fcffe700b81d7be89d60c7ba4f1
ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351
-PODFILE CHECKSUM: c9ace43f8ce76d66d3a7c91dea869911207c0354
+PODFILE CHECKSUM: 775f5c8c488024e24d494aad6331e9fef8f9e2e5
COCOAPODS: 1.16.2
diff --git a/ReadViewDemo/Pods/Pods.xcodeproj/project.pbxproj b/ReadViewDemo/Pods/Pods.xcodeproj/project.pbxproj
index c551224..9ff3843 100644
--- a/ReadViewDemo/Pods/Pods.xcodeproj/project.pbxproj
+++ b/ReadViewDemo/Pods/Pods.xcodeproj/project.pbxproj
@@ -7,490 +7,491 @@
objects = {
/* Begin PBXBuildFile section */
- 001537CD7DF168D7360811789C91CD46 /* DTAnimatedGIF.h in Headers */ = {isa = PBXBuildFile; fileRef = A9FBC69C7DA29A7283920FDA9EBBCE8A /* DTAnimatedGIF.h */; settings = {ATTRIBUTES = (Public, ); }; };
002685407B9C4D548571DA24AFD6E8F1 /* NSString+Paragraphs.m in Sources */ = {isa = PBXBuildFile; fileRef = F53CCB60DE5D06A43E4A52EDB274E7D2 /* NSString+Paragraphs.m */; };
- 0058C6D615D0C6A6DF83E674747C3F7A /* NSURL+DTComparing.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BFCAC7C2AEA867E67C63A852576774F /* NSURL+DTComparing.m */; };
- 012DAC181C6B561981FCE303B0851DB8 /* ConstraintDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6578DFAB51436E8DF266FD087700E26B /* ConstraintDescription.swift */; };
014449AE560FFF3C628FAE639D043004 /* DTAnchorHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D63AE1801C7AB149219317BE7529B26 /* DTAnchorHTMLElement.m */; };
- 036D334C0439D9A870AE3715E03C2A43 /* DTCustomColoredAccessory.h in Headers */ = {isa = PBXBuildFile; fileRef = 33DB57DF8C5AE334B1BBE9F989352F1D /* DTCustomColoredAccessory.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 02D54925050EDE70FB81E1C369E5D68B /* NSURL+DTComparing.h in Headers */ = {isa = PBXBuildFile; fileRef = 731C0C61E3AD227D691FBA1C2C17BF7B /* NSURL+DTComparing.h */; settings = {ATTRIBUTES = (Public, ); }; };
03D90C831397E8636A33CE557024A136 /* NSMutableAttributedString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = A2C299CFB47AA6ECF86F7348E1EB751F /* NSMutableAttributedString+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 0478D50AEAB005CE7C819FD67086C43F /* SSAlertAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D2EC0B845131A88247902AEA26F40AE /* SSAlertAnimation.swift */; };
+ 040D05A4014324F2B7CFE3C1F6FA2A2C /* RDReaderPageChildViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12DF060DFD7CF87B6793F3F4F8B01F53 /* RDReaderPageChildViewController.swift */; };
+ 0474E2714676F7C1A04FD670227D085C /* RDReaderEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 253099942A3CAA07578C7C4CEF760BAF /* RDReaderEditView.swift */; };
0561DF6793C8516F469D9813F5CE3211 /* DTCoreTextLayouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D6DA580F40DF113709E15CF6542A35C /* DTCoreTextLayouter.m */; };
- 06EC253538D2D74E3D83D285CC29D31A /* Archive+WritingDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C7359A7C3FC7675FED220200A43A17 /* Archive+WritingDeprecated.swift */; };
- 08095AE8474BA1291A5C3ED32E0720CD /* RDEPUBTextContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51ED414F31FCA48C76821B337DDAAAB /* RDEPUBTextContentView.swift */; };
- 0859A62027347E929A19720028CBC2F1 /* Archive+Writing.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF988440049800DB7824A94B81D20254 /* Archive+Writing.swift */; };
+ 05FD36D1800EE1EE1FA38E28D0840716 /* RDReaderGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F3D8D4765924AA777C5153C89603E9 /* RDReaderGestureController.swift */; };
+ 066CE5C759FA03F1041F13550A6052FE /* ConstraintDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6578DFAB51436E8DF266FD087700E26B /* ConstraintDescription.swift */; };
0AAA5BB1DBF584102B15CF8738F1815E /* NSScanner+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = CA538A48DD3691701EC6B5021D7B7381 /* NSScanner+HTML.m */; };
0ABC4D62588EABAA636441FD9A82C940 /* CTLineUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = DD58A1908F3C532210EA65ACE410A44D /* CTLineUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
0B6FA1F22150D7F8B1865F41FF70C922 /* DTImage+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AB9016408894675D11069CDAACEDE94 /* DTImage+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 0C13480D3B9AFAE9809CD2D2E5F2D98C /* RDReaderCommon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7E829F5BEE0B03156583C8500D6913 /* RDReaderCommon.swift */; };
- 0EA713DB846478646ABBA420700C9D54 /* RDEPUBWebView+Reflowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A460891E141702B09E55886799672F5 /* RDEPUBWebView+Reflowable.swift */; };
- 0F0B319A800024F0B16CA7595BE2578A /* RDEPUBWebContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B0DCA31DB8359A87C75F5CB5E58893A /* RDEPUBWebContentView.swift */; };
- 0F3629238F8353ED017BF9D21686FA26 /* DTFolderMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = B245F7E143F3091652C64F6593E33F7A /* DTFolderMonitor.m */; };
+ 0D207CC83844816FD8567E5EFDD828BF /* RDReaderToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5DB66CA8931324F68042711F94DFC9 /* RDReaderToolView.swift */; };
+ 0E470E45E8308EF273AE556C6C555A66 /* ConstraintRelatableTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885653999BD237A574B67DB279DE7E87 /* ConstraintRelatableTarget.swift */; };
0FCBC8C64D8862F2A0E8794EDADDECBD /* UIFont+DTCoreText.h in Headers */ = {isa = PBXBuildFile; fileRef = 647FB0C5AAF84C71A7784DD277E48910 /* UIFont+DTCoreText.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 10058A69247B9D1AD29A2C751382FB2D /* RDEPUBReaderTopToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BCBA99577F4C792AAF4111AD4D40010 /* RDEPUBReaderTopToolView.swift */; };
100BFFBD24F234C095EDB001B8088DB1 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = D1599698B5A62671C488F13E07272B09 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.m */; };
109458F9D48FD4F381F0E40E2DB3D91C /* DTLazyImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = E5F69A1E1260BAD7737BEB533F643A15 /* DTLazyImageView.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 1113929DD8BAF5A05BC04B242CF7DFB0 /* ConstraintDirectionalInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91144FC099DD458E61625CD939577DAA /* ConstraintDirectionalInsets.swift */; };
+ 111D6ED690090E677CDB4B6257F136EA /* DTExtendedFileAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 538572A5C098252D629F2185F1B1C9E8 /* DTExtendedFileAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 11749E414D9856E3D012661C8EB1AB0F /* RDEPUBTextRendererSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76B26D09BDAD5ED7601C6B3F8E6012FC /* RDEPUBTextRendererSupport.swift */; };
11AB1902A68385851FB81E870991F9A0 /* DTImageTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = FEEB15F87BA6575BBFE1C5FD19B17CD7 /* DTImageTextAttachment.m */; };
+ 11E013BFA2329DF9A04AE134BE341AB6 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 53D473C6F07982D3D25781A343EC5A99 /* PrivacyInfo.xcprivacy */; };
+ 126F7FE4B3F6AB263AD88884DB5989D4 /* RDReaderContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE77B2BBE5104C1EA5F4E5AD2EB96D86 /* RDReaderContentCell.swift */; };
12B4A35B8E40C112E18A7C5511F023E9 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4472755F83C121DE5E1E505D145F6DBE /* CoreText.framework */; };
130C3A85D9F9F445222C2E31EC245B0A /* NSScanner+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = A0927A875CA1F11DBCE60DFC6F0CA0B6 /* NSScanner+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 135CDB61A034B664CEEEE099179FC25F /* NSString+DTPaths.h in Headers */ = {isa = PBXBuildFile; fileRef = FC5EC7C3D1808C9CF53354D7A3D69EE7 /* NSString+DTPaths.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 13907F372647BF359E0B9DB568F6195C /* RDEPUBReaderBottomToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C933E9282740096F1DEA7BC177C4CE98 /* RDEPUBReaderBottomToolView.swift */; };
- 1484275BE442498A49C250052272516A /* UIImage+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = D85A85D891A320165843EF9F489178F7 /* UIImage+DTFoundation.m */; };
14BDD9FB7A7AC5C8ABFF8D24C381F3DA /* NSAttributedString+DTCoreText.m in Sources */ = {isa = PBXBuildFile; fileRef = DBC9280BA00935565E9658775DD7404D /* NSAttributedString+DTCoreText.m */; };
- 14CAFC640E5214CCE3495931D69D096C /* SSEventTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0644499342927A4A397DE2FBDDE27D /* SSEventTrigger.swift */; };
- 158A94C8261BC176E28FE367E1838222 /* RDEPUBRenderRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5861AF411361E4F80DAC87746AF147 /* RDEPUBRenderRequest.swift */; };
+ 154ED4E5865E3E56A883B45F1A067E7E /* DTTiledLayerWithoutFade.h in Headers */ = {isa = PBXBuildFile; fileRef = DF5EDCA3B21964D0462BE0982E2F0BF8 /* DTTiledLayerWithoutFade.h */; settings = {ATTRIBUTES = (Public, ); }; };
15A7ED15DA03DD3B9F26A017D270D1C2 /* DTCSSListStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = FDD333B409D528E5006FA48F4D70A6F5 /* DTCSSListStyle.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 15EBCFBB5159F08DFE7BEFFEE3D738FA /* ConstraintLayoutSupportDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5B34EE2160C27947D1EEFD42184CF5D /* ConstraintLayoutSupportDSL.swift */; };
+ 167EEB3313680E7E471643A70D1827C8 /* RDReaderView-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 452EABEB04AADDB7964F46326FBDA5BE /* RDReaderView-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 1681783E9D3402C9DD8ABC36E57E546D /* Entry+ZIP64.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345CE45F696634CEA692A86D16F684C /* Entry+ZIP64.swift */; };
+ 174BE62D96EB07EAA933983515554925 /* NSData+DTCrypto.m in Sources */ = {isa = PBXBuildFile; fileRef = 17F750F38183302BF345F40F5A4CDD43 /* NSData+DTCrypto.m */; };
+ 18C30D439A1808FEE5485B2FCFBE1EDD /* DTBlockFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = A0BD48DAF5AE9DB087F2552E99069709 /* DTBlockFunctions.h */; settings = {ATTRIBUTES = (Public, ); }; };
191824B2B1E1B0A7DD3B6298100B4B8A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE4821B98DDE6C71411EC139EC6D2762 /* CoreGraphics.framework */; };
+ 195562418D0ECF2A016147D05DD2FB14 /* RDEPUBReaderTableOfContentsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA5B318B35A219FECD75BB57B333EA6 /* RDEPUBReaderTableOfContentsItem.swift */; };
19649ED312D92A6F5D2D6AF4E540CE75 /* DTHorizontalRuleHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = E28CA95D89DBAB2FE56BC8513D5838D7 /* DTHorizontalRuleHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 1A8EAA40443EC0FF4C3E2C1523A2ACAA /* RDReaderView-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 60EFBE79C0CB86069AE38B09BCF5E216 /* RDReaderView-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 1B8D5F7759D35AD412799D8462DE6EF2 /* SnapKit-SnapKit_Privacy in Resources */ = {isa = PBXBuildFile; fileRef = B9DCB5EC0B1CDADD221717CADDF62359 /* SnapKit-SnapKit_Privacy */; };
- 1BB519D7D473648B38F9FC65780E0C81 /* ConstraintLayoutGuide+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A9A82CB094636AFB53D6D130D6219BA /* ConstraintLayoutGuide+Extensions.swift */; };
- 1D0A546B5297D47941B55E3D0C3A0DC8 /* NSDictionary+DTError.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC368BD2A8D266EB771E5FB9A5A5B3C /* NSDictionary+DTError.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 19DF9BF958E8F8BCD6D9A7AEDD1D6B9F /* NSURL+DTAppLinks.h in Headers */ = {isa = PBXBuildFile; fileRef = 557DA029E15EC99631B647AE8922007F /* NSURL+DTAppLinks.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 1A56D0D3AA658B3AB9E39B9880A1AE3D /* NSURL+DTComparing.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BFCAC7C2AEA867E67C63A852576774F /* NSURL+DTComparing.m */; };
+ 1ADD0DA79D32780DAD95C47DD3285D3A /* RDEPUBReaderBottomToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24330A8818509BED791AACF25F76FAC5 /* RDEPUBReaderBottomToolView.swift */; };
+ 1BEFAA653E1363766ADA5853B2CC46E3 /* RDEPUBParser+TOC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A107E97C03EEEAF221E2E6A7DE9D530 /* RDEPUBParser+TOC.swift */; };
+ 1C52D988C01D7347E3E09CFCABE0D252 /* RDEPUBTextPaging.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF349E342BDEE7C2FEC73AAA28075DD9 /* RDEPUBTextPaging.swift */; };
+ 1CE2F282080E62BD0C1F2F1E53076D48 /* NSString+DTURLEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = CB7F3535507F56824BA21509302CF8B3 /* NSString+DTURLEncoding.m */; };
1D460603DCA4A4E5ECFE8FF1F1CACC10 /* DTColorFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = AFC4C9F69BF2D0CAEBAA83896C139D2C /* DTColorFunctions.m */; };
- 1EA660E3DB3DDFEFF2EDE06A605E3CA6 /* DTFolderMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 2101691D5743105B2E3E40376584B91C /* DTFolderMonitor.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 20138D4B6A869120A10A541037105297 /* RDReaderPageChildViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B825BF76778E9B12A926734F1AF152 /* RDReaderPageChildViewController.swift */; };
- 20DD910E27D7A96E940EA57AD7DC3C34 /* RDEPUBDTCoreTextRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FBA6E5012B155499FA6DFF100BC7D79 /* RDEPUBDTCoreTextRenderer.swift */; };
- 211AE27B48917450A46BFEE99C9783B1 /* Entry+ZIP64.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345CE45F696634CEA692A86D16F684C /* Entry+ZIP64.swift */; };
- 219CD98D5C72DD93259643576AD89D47 /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD081272B47A3B73CE693D21DC26827 /* Entry.swift */; };
+ 1D600CC8DBB76DEEA758ECA4DD45F277 /* RDReaderTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DE57FE714CC9010A030F3AB1AF8B8CC /* RDReaderTheme.swift */; };
+ 1ED2B410D8F238607123A8C6B7A126A6 /* RDEPUBWebView+FixedLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAFF0D063505DC0420E372684DDD6497 /* RDEPUBWebView+FixedLayout.swift */; };
+ 1F5FBFB3F78AED06A441A8ADDD015579 /* ZIPFoundation-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = D927F128EBF26AD2C61FD0E6B6F6F5ED /* ZIPFoundation-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 20E9235E93A1DF012036B350B0831BA9 /* RDEPUBSearchEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D2E0632761004082322B9F4EDC63E17 /* RDEPUBSearchEngine.swift */; };
2203356EEA94A9172BC7902923E43CBE /* DTCoreTextFontCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B7771FDE9AB1688166584C0AA03F12A /* DTCoreTextFontCollection.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 2297FEEBB72AF884A553482158C5307F /* Entry+Serialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F991240340FA026C71B68EB25B9F9C8 /* Entry+Serialization.swift */; };
- 233BFC812028ED5784107F4BA1A37B80 /* Data+Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5964AF1336E2F0DA0E2C32991E76C78C /* Data+Compression.swift */; };
- 239C89E10903E9FFC7C10CCC5AA718E0 /* DTPieProgressIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DA1E8494B86FFD79C639DCB8C21E1BB /* DTPieProgressIndicator.m */; };
- 23A5611E4A0C760F58A55801E273C725 /* DTHTMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D75FEDE65722CF41F7F32A164376926 /* DTHTMLParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 231E483DAB1DEA6422A486819622EEE0 /* SSAlertSwift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 937CEFA81DA4227008DBFB114783A08B /* SSAlertSwift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
24395BEB3C360D9DA5180BFB955B04A0 /* DTAttributedLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 21BAA93E67D8CBC5297302D798FB4F7A /* DTAttributedLabel.m */; };
2445709667CB3820E51CD4AA915E6943 /* DTHTMLParserTextNode.h in Headers */ = {isa = PBXBuildFile; fileRef = C30D143134EEC5D4BA99459104B76A2E /* DTHTMLParserTextNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 24BE8B67DFC1D9F0C48DED102C4133AE /* DTFolderMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 2101691D5743105B2E3E40376584B91C /* DTFolderMonitor.h */; settings = {ATTRIBUTES = (Public, ); }; };
24C5B52FD2AA9C49731DC6859ABA7CDE /* NSDictionary+DTCoreText.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C50CAB4241C141EF6EC9ED22AB3A7E8 /* NSDictionary+DTCoreText.m */; };
+ 25ACE4882742112A2C01E96CA16617EB /* DTCustomColoredAccessory.h in Headers */ = {isa = PBXBuildFile; fileRef = 33DB57DF8C5AE334B1BBE9F989352F1D /* DTCustomColoredAccessory.h */; settings = {ATTRIBUTES = (Public, ); }; };
270C5C10028F0BC5A62548326CCB379B /* DTColor+Compatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = E196F56E68578181D5BDE787DE5C0F3A /* DTColor+Compatibility.m */; };
- 27EF527E4F7333325D5BEFA0C282E79D /* RDReaderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1CE6128AF50B6B5D97ABA06FF53EEB4 /* RDReaderController.swift */; };
- 2927D96D0C48E79CD0FF330A18F73DAA /* UIScreen+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F051D813D50D20D53205D5FDCAA877C /* UIScreen+DTFoundation.m */; };
+ 276032CDEF337C09E09F4DA5AD70124B /* FileManager+ZIPDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F9B298989305B9569B71E448E8C1D4 /* FileManager+ZIPDeprecated.swift */; };
+ 279BE4E8F9F8D94321C1CC26E8BB93B7 /* Date+ZIP.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76A97683FB5E8E8B86EA4AD71F6F4B4 /* Date+ZIP.swift */; };
29D9B32DFFC59A157EDEF005DB92A5AC /* DTCoreTextLayoutLine.m in Sources */ = {isa = PBXBuildFile; fileRef = C71D83D6AEF198FA680B8A80BF2D9605 /* DTCoreTextLayoutLine.m */; };
+ 29EDF9E88B3E16B072F2CCB1C7DEAA64 /* DTSmartPagingScrollView.h in Headers */ = {isa = PBXBuildFile; fileRef = D2BA6D5DBD4C6A9285C4592721790A26 /* DTSmartPagingScrollView.h */; settings = {ATTRIBUTES = (Public, ); }; };
2AAF9361720FC2D8FFCA93B5FAE42019 /* NSMutableString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 31F3F91D5624C451E5D44D85B1DDE7C5 /* NSMutableString+HTML.m */; };
- 2BD299F0A5A8D39EA8A2BABAA693779D /* RDEPUBWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F415D96FD347CA491C7F2F5C083450DF /* RDEPUBWebView.swift */; };
- 2C566517483499C988B7EA68011096A4 /* DTExtendedFileAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = F5389D4E96A4AFE175691B4D3D4524E7 /* DTExtendedFileAttributes.m */; };
- 2E533EA900C9E2CF311154CF9A27E63D /* Archive+ZIP64.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5693BD171B879406EA14C97F4AD8D8 /* Archive+ZIP64.swift */; };
+ 2CC5E7338D759DD787248B104E10FACF /* RDReaderView-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 19110636AAFDFF4EB74845D9AFAE5A4E /* RDReaderView-dummy.m */; };
+ 2D8D1A6E4C0DA17CE2A98B62E53CE767 /* RDEPUBReadingModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B653A342BD8F2FB2D1C08897F3828B2F /* RDEPUBReadingModels.swift */; };
+ 2E2411C2B4447E483F70670019CA63E9 /* ConstraintOffsetTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F950CEF31AC0065C0BEAB26BA2CEFA1 /* ConstraintOffsetTarget.swift */; };
+ 2E2A2C6DC21AB0420F130F1966D089C5 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0834E84632886CDD9911CAB76183F1CE /* QuartzCore.framework */; };
+ 2F4CB3D2A68A718B41D2C065704A8FA9 /* UILayoutSupport+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C647A2EB2720BCBF88B160D124B57BF /* UILayoutSupport+Extensions.swift */; };
2FD7360250C2255A80583D4CF518FBFA /* DTTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = F35039A8D0AA1F7BAFC4E5E016057E15 /* DTTextAttachment.m */; };
+ 304D5335C26F067080B2311315B1029F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; };
30C01FE491C26314F776AD8B2AF136FA /* NSAttributedStringRunDelegates.m in Sources */ = {isa = PBXBuildFile; fileRef = E6F2E79CC33E0823C4E719ECDDFBBE08 /* NSAttributedStringRunDelegates.m */; };
- 310F4B84711895989B5168C8D52D15D5 /* RDEPUBParser+Archive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D9A9E0B80E0DED0838BD11BF64B6A4B /* RDEPUBParser+Archive.swift */; };
+ 30E870BE77BA4929D69B70D725B624D4 /* FileManager+ZIP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27B7D3C6F0C845E3D42BBA613FFB0465 /* FileManager+ZIP.swift */; };
+ 313F50E37DAEC0397E8B0E03B2590A2A /* DTFoundation-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 5FAB3C7F19A511C2B1B7DFBA3CA308C4 /* DTFoundation-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
31B805C50E0E187E6242B0C878C4D30D /* DTCoreTextMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5D34047BAA894BEF1DFA23E6A9DAB1 /* DTCoreTextMacros.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 33B5B4CE208C764A7EBD490B7D2E21F8 /* RDEPUBReaderPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41ABB15C697798810A88528D70798D94 /* RDEPUBReaderPersistence.swift */; };
+ 32FA30357E7F639FB657347B9F0BFE05 /* RDEPUBParser+ReadingProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD631F7E7CAEA1AF51208055E9B3D5D /* RDEPUBParser+ReadingProfile.swift */; };
+ 33DE84492517C8196278DECFEF76D4D1 /* RDEPUBTextLayoutFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25752F6845CAD02E33A5C41D9591BD7C /* RDEPUBTextLayoutFrame.swift */; };
343E794F88AA503F5139C2244DC1A2B1 /* DTTextAttachmentHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = C298199F5DC5390E8B6666F638B34C10 /* DTTextAttachmentHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 35A9265CEAFF87AD0E44185FF361493B /* RDReaderGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF77ED14D8C4644049B6FD2CC631B2E4 /* RDReaderGestureController.swift */; };
- 35BD5A1BF75ADC850197D7A59E38A171 /* UIView+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4219E7C32019899890CAB66E5FBDF524 /* UIView+DTFoundation.m */; };
+ 347AF0E8E8C418B6D2174CDDD9E63A10 /* DTFoundationConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD568D38C209BA1CC0EBFA809A35204 /* DTFoundationConstants.m */; };
+ 34811E26E3D0FEC7265CF65B2A84E597 /* RDEPUBWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80722DEDCE690F56237D5706BCEA3B55 /* RDEPUBWebView.swift */; };
+ 352E2D0CAB5AC80CFEE75AE900414065 /* SSAlertAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D2EC0B845131A88247902AEA26F40AE /* SSAlertAnimation.swift */; };
35C5BE59CA712CCDF93D6E7010D93883 /* DTAttributedTextContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = C123C127CC539ACCB510F665DB774899 /* DTAttributedTextContentView.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 366CBF9BD44FB51B8E8516773F28D2B1 /* ConstraintRelatableTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885653999BD237A574B67DB279DE7E87 /* ConstraintRelatableTarget.swift */; };
- 36B884FA41926BFC450AAAB5C38A7CFD /* SSAlertCommonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE4EE750340347ED584C5A2C1164331A /* SSAlertCommonView.swift */; };
+ 3653C516365D520640BBE562F9413873 /* RDEPUBNavigatorLayoutContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 488C9F0D7B36673ABF0CAFECBB38D91D /* RDEPUBNavigatorLayoutContext.swift */; };
+ 36978F27A35E63A4EB3BB12A0ED0F45C /* NSString+DTURLEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 31BD7533867336DEA9B835F471767178 /* NSString+DTURLEncoding.h */; settings = {ATTRIBUTES = (Public, ); }; };
372E907DF08263D0E1AF904F1FCD66EB /* DTTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D36ED8209C907E3E9B074DDB399F54FF /* DTTextAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 3834AEF9319FE775ED5033D2B986F021 /* DTTiledLayerWithoutFade.m in Sources */ = {isa = PBXBuildFile; fileRef = 70F5D03B9B3B7F182C8F5C1F5D4FA7E7 /* DTTiledLayerWithoutFade.m */; };
389F7C8400F9223C019F2D7825CD2369 /* NSCharacterSet+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 713C9FD0354D16AFCCFA630A97F2DA68 /* NSCharacterSet+HTML.m */; };
- 393C0F87C15F797F78A7B24F56C5150B /* NSString+DTUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = BE547E464E5D34F9FFB24BAC81D1E4CC /* NSString+DTUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 3B109CB6702017A9EF8A9C9F53928FC8 /* DTFoundationConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD568D38C209BA1CC0EBFA809A35204 /* DTFoundationConstants.m */; };
- 3C86D29AF96FA66B92A9AC310F40D4F6 /* UILayoutSupport+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C647A2EB2720BCBF88B160D124B57BF /* UILayoutSupport+Extensions.swift */; };
- 3D7D6393CE6FDECD70C316D45EA9D9C6 /* RDReaderFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC8A02C88E03D6E08BAC3B5313BC4E9 /* RDReaderFlowLayout.swift */; };
+ 39E64E1AEFDF71D92BE6C748943D2905 /* NSArray+DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = 73940D215D644B6815A09DA802CFBDDF /* NSArray+DTError.m */; };
+ 3B517F6D52621AE31921A1C670FAED7F /* RDReaderView-RDReaderViewAssets in Resources */ = {isa = PBXBuildFile; fileRef = 83410CC9CF2ABE63B90A92F2F988BF65 /* RDReaderView-RDReaderViewAssets */; };
+ 3C5B4C8420F80D49E6A5E44EDC8BCA6E /* Debugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D495E2AB1223BD4D4909F8FF1FF47DAF /* Debugging.swift */; };
+ 3C7562A51DCA0E5B8DCB3B8FE76C4171 /* RDEPUBReaderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF6882DA7E15CAFF5E16D073887079F /* RDEPUBReaderController.swift */; };
3DC8F9474FC6A0BB7E81A703CE958966 /* NSAttributedString+DTDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 71E2D85A099C99CD82FD1842EE7C52D2 /* NSAttributedString+DTDebug.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 3FD8C9235E83F46FD026EC045A0ABD91 /* NSData+DTCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 02EDFE0EEA47398F3E1A361A73AF6A6C /* NSData+DTCrypto.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 406E9C581DA32F1149ECE217F536B552 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEB14A8F8170777DB7CCC3F27C8EA57A /* ImageIO.framework */; };
- 40844C59C5D17D3D33E1DCEA1D2376CB /* ConstraintMakerRelatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD768C27E392C934C06DFB4AC401037C /* ConstraintMakerRelatable.swift */; };
- 42766B7F491DAA4F6CCB2D71162F9A53 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; };
- 42CFA997A1AE3BD8827EF8D8518DA1E2 /* Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0EBAC7714A5F73B75AAB47D24B08B71 /* Constraint.swift */; };
- 444CF6C0CC76B0427DFF5C63D65D8095 /* RDEPUBResourceResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22030AADBBCAFBE09AB252BEA16E972 /* RDEPUBResourceResolver.swift */; };
+ 3FD419F6ED04B286B3A2EDB08087BEFE /* RDEPUBTextBookBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33D41502ACD101EC5DFBB42664EF720F /* RDEPUBTextBookBuilder.swift */; };
+ 4034ACDAF443AF79409D59FC02551B04 /* RDEPUBWebView+JavaScriptBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C48433D1C85ADDC616F469F5369EBF1 /* RDEPUBWebView+JavaScriptBridge.swift */; };
+ 40FD1597D083DD699D3F69CEEA0CF1E9 /* NSMutableArray+DTMoving.h in Headers */ = {isa = PBXBuildFile; fileRef = B0AC307F12521444C973EA0DE38B4608 /* NSMutableArray+DTMoving.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 413F3434C4330A19E7D3559A448A2F5B /* DTActivityTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EEFAE80193E1F0DA53400C9AD641410 /* DTActivityTitleView.m */; };
+ 4241A470F92AFB5B33989AA5F063016C /* ConstraintMakerExtendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5245010774F8F1C4857763CC7B96E7B4 /* ConstraintMakerExtendable.swift */; };
+ 427E3037E37AEEB515A838B9C03CCAD6 /* RDReaderEPUBContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68492190D405DF22F3253362857EFBA3 /* RDReaderEPUBContentView.swift */; };
+ 4298602E28DD36D7B40E43467A66BF03 /* SSAlertAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97995C73297871C353364E685939DED9 /* SSAlertAnimationController.swift */; };
+ 42B388F12A2019A57A25911F0503A8BA /* RDEPUBReaderSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB4E7A980EAE119E01B275C4EC601170 /* RDEPUBReaderSettingsViewController.swift */; };
+ 44A7556485A5D76380032C6D1E30385A /* NSString+DTUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = BE547E464E5D34F9FFB24BAC81D1E4CC /* NSString+DTUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; };
44B1FBF893C06F314F8C9BD1405897D8 /* DTCoreTextGlyphRun.m in Sources */ = {isa = PBXBuildFile; fileRef = 36FCB869246FE45FD1C9609A34EB4E74 /* DTCoreTextGlyphRun.m */; };
45133E47CC5FBCF78BFCD38981BA027F /* DTDictationPlaceholderView.h in Headers */ = {isa = PBXBuildFile; fileRef = 982391EC8620A617B8788C8A6101C96A /* DTDictationPlaceholderView.h */; settings = {ATTRIBUTES = (Public, ); }; };
45F90B98546FDBA03AB112DFE859A185 /* DTCSSStylesheet.m in Sources */ = {isa = PBXBuildFile; fileRef = E0D90EB06FC37D534934542F9CAB1D74 /* DTCSSStylesheet.m */; };
+ 46AB2EB766BE04E4CAFE4AD370832C01 /* RDReaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BCEA44CE11268C0D82A87D73B9FC906 /* RDReaderView.swift */; };
46EF32D586FF590CE2C60C66364B9357 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEB14A8F8170777DB7CCC3F27C8EA57A /* ImageIO.framework */; };
- 47ADABE0B603EE399007EA9458217FCD /* RDURLReaderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6061E339364E22A1C8EC52EB3E7BAB0C /* RDURLReaderController.swift */; };
- 47B4C24C814C055F6084E47CB16AA2FB /* DTSmartPagingScrollView.h in Headers */ = {isa = PBXBuildFile; fileRef = D2BA6D5DBD4C6A9285C4592721790A26 /* DTSmartPagingScrollView.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 481E79322DFE50499DFC1CEEB1294C19 /* RDEPUBReadingModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1742D747372CA5272389D6847267DA60 /* RDEPUBReadingModels.swift */; };
- 48D491D1C7A2785AD67BF7A903F0BD63 /* DTSmartPagingScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 96EEB6B022E22A4ABC86D87469D4815B /* DTSmartPagingScrollView.m */; };
- 49E8CA0ED4B006303DBF9F0C9ADB22CF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; };
+ 47AB5D23E6413FA8C557C81121B469FC /* RDReaderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D5F97E4E9A92968E446569772486F37 /* RDReaderManager.swift */; };
+ 4804C1C057F825E59C73EB4BD6C363D9 /* DTHTMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FB2B1B0498DCBE7B79BDF49E963E3D /* DTHTMLParser.m */; };
+ 486921E9BA067635BD85C70DF2D7F569 /* ConstraintLayoutGuideDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4684B406B325B35F39322D75B59B6479 /* ConstraintLayoutGuideDSL.swift */; };
+ 492A61D4D0840D6EB1BACB6B959B232A /* Entry+Serialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F991240340FA026C71B68EB25B9F9C8 /* Entry+Serialization.swift */; };
4A13B4E5EA8616A8F09D90BC6EAEEA64 /* CTLineUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = D24FE8634A7AD2562DB8ADB9D4BBB36A /* CTLineUtils.m */; };
- 4BDA415C75147B7648F8EF91EEC24A89 /* RDReaderEndView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58281C9CDEC881CBF5E6D28D119E5D23 /* RDReaderEndView.swift */; };
- 4C549C5FDDC6DBE0744B4D0409356E19 /* RDEPUBReaderHighlightsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 914EF000CE6CCD6816D414353BD6A3EB /* RDEPUBReaderHighlightsViewController.swift */; };
+ 4A200E58DD0CD905C56E68CADEFB89AA /* ConstraintConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7176B677A350927FC670368313DC2CA1 /* ConstraintConfig.swift */; };
+ 4B5F2AAB7391622A0C28A45F084C6725 /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD081272B47A3B73CE693D21DC26827 /* Entry.swift */; };
+ 4D6F3F74103D6FA619464876B07580EA /* ConstraintInsetTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A5ABB8F3656A110B955B0306E9A8C3 /* ConstraintInsetTarget.swift */; };
4DA6EC46F8968265B96C6D7B2260C4E5 /* DTTextBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = ADC5AC8FD4EC45E9BA5654A38715BB93 /* DTTextBlock.m */; };
- 4E1EF5E49879FBA5DED5C0AF8997082F /* RDEPUBResourceURLSchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031EBE9DD66645BDA304759CCB59C833 /* RDEPUBResourceURLSchemeHandler.swift */; };
4E4939F6592B06D2CA764DCACFB47871 /* NSString+Paragraphs.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E37DC74BB53F90A5AD54842858A62F /* NSString+Paragraphs.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 4ED7ABD4E7E0A75CC7DC9FFF7275C40C /* ConstraintMakerFinalizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B32EA4B7A492EEF26CE260E9AEFA0B /* ConstraintMakerFinalizable.swift */; };
4F50C703427747B88405080B221349A1 /* DTLinkButton.h in Headers */ = {isa = PBXBuildFile; fileRef = D7B5E61166782ED8D585D4FCEE2074DB /* DTLinkButton.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 4FFFDBB1FD4814492870E2C6E1C5C8F9 /* RDEPUBReaderSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704B13E62616203BD58554B42C37B039 /* RDEPUBReaderSettings.swift */; };
+ 4FEB2A6CDDDFDE2FA58DAD025EAACC5B /* RDReaderEndView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4339C27231D120EC2EC86F021CF5DC16 /* RDReaderEndView.swift */; };
5065D99E1588E8CC60677E74916A5C03 /* DTAttributedTextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E1F63A854BBF28E93AC1D3F71CD48CBE /* DTAttributedTextCell.m */; };
- 518231B6910D152A1C7CDFA27CA86162 /* SSAlertSwift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DE4FA5133891593CB45AF36B8EBF60C /* SSAlertSwift-dummy.m */; };
- 518A72D46FFBF49A9BC6072929F9BCA7 /* SSAlertSwift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 937CEFA81DA4227008DBFB114783A08B /* SSAlertSwift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 50BEEA8526E3FFEC2C656963A974FC6E /* NSFileWrapper+DTCopying.m in Sources */ = {isa = PBXBuildFile; fileRef = 1756C6FFCA695290BA62DA4E7B6DD20B /* NSFileWrapper+DTCopying.m */; };
+ 5138F3A988B329DCA557AF44DD3B23EC /* RDReaderTopToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38848FEE38CF2D58126C89D2B90F17C3 /* RDReaderTopToolView.swift */; };
+ 51668329A730D10A580B821652A305D7 /* RDEPUBWebViewDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB800E7F2C56A31E6995405B8BBF8027 /* RDEPUBWebViewDebug.swift */; };
51A1B5800D23C355B1712E8D1103780C /* DTTextBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = D497C3CA364E37DAAFF2C3B90CC7DBE8 /* DTTextBlock.h */; settings = {ATTRIBUTES = (Public, ); }; };
528DBAC7FCEFC7D6575C1F6CB4399AEA /* DTStylesheetHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 96FA99AA7D4AD0D4246C8DAC1CC0C24E /* DTStylesheetHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 52CE2843805164C3F6F2908F5D9AE422 /* RDEPUBReadingSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621689764606AC8062EBDE6D594D7829 /* RDEPUBReadingSession.swift */; };
- 53563F1CFAB0421BAD0A2D2B1D9A3E84 /* NSDictionary+DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = C18480913E002315E3F5EC6945371697 /* NSDictionary+DTError.m */; };
- 543288E511F5F8D2FDEC2E7E3B32BE4A /* UIViewFrameExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C351514678D41C58869BD057D5483F /* UIViewFrameExtension.swift */; };
- 5442847FF11F8B9D3FE55E2E8E25712B /* RDEPUBWebView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03541C9357524E19B174851FCAEA660E /* RDEPUBWebView+Configuration.swift */; };
- 5469E4003D5103C26EA9830F9E3837EA /* SnapKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B10D3EC1701BBA4D69926915F942558 /* SnapKit-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 52C7D60AFC4413B845545007B390E184 /* NSURL+DTUnshorten.h in Headers */ = {isa = PBXBuildFile; fileRef = 054674EB90C625BB4BEA9368ED92592B /* NSURL+DTUnshorten.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 52E87EE9158C24ED119C4964B0396A89 /* NSString+DTPaths.m in Sources */ = {isa = PBXBuildFile; fileRef = A3565FEE19F19CB43A43409AFFF2BD2C /* NSString+DTPaths.m */; };
5582735073EBB9F5078E6E8AEEFB646A /* DTCoreTextLayoutLine.h in Headers */ = {isa = PBXBuildFile; fileRef = 64AB89EAB10148C39B726B26BE04F4F8 /* DTCoreTextLayoutLine.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 55EAC731D361D5034E2DD7BEA1967C3A /* NSURL+DTUnshorten.m in Sources */ = {isa = PBXBuildFile; fileRef = C80065DBA5634DAA71129404975D7349 /* NSURL+DTUnshorten.m */; };
- 562C1F47388C41F8A1A82EFF37C7180D /* ZIPFoundation-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = D927F128EBF26AD2C61FD0E6B6F6F5ED /* ZIPFoundation-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 56DE497D0143A29CBE2BEDC5C62B57F0 /* LayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932FD688DE493323BA6B691BC2CB5094 /* LayoutConstraint.swift */; };
- 57B5F7F51C8FA3D4D308FE4D9FA37FCE /* ConstraintRelation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3593ADF561D422F73C8EABAD4A4F440 /* ConstraintRelation.swift */; };
+ 55CA1C7D7B09E9AA9D5B47EA560A8057 /* SSAlertViewExtention.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2280F2302F54E90122747821FE681543 /* SSAlertViewExtention.swift */; };
+ 56A14FD7CE827CF4712C174535F81A6F /* SSEventTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676181142A1274D18FC2C2905C411080 /* SSEventTrigger.swift */; };
+ 572899835C3B81EA0CF84E5601B62508 /* ZIPFoundation-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A06A36ADBE678DB44158D32E751FAFF /* ZIPFoundation-dummy.m */; };
+ 578A991941D95E9D43BE65F815398129 /* Archive+Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA251BAC8B430B3A4D4C4321046B990 /* Archive+Deprecated.swift */; };
57D5228C8A85628772D4D78E7FBEAF3C /* DTDictationPlaceholderTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = 477A0FC2A1F0B7F7CCF72D201ECEBAB0 /* DTDictationPlaceholderTextAttachment.m */; };
- 5870684F0BF8C4D8DA54A91E8A5EC405 /* Data+Serialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC78D0B0F37BF9AD1058CACA9AAF9BE /* Data+Serialization.swift */; };
- 58C1FA1E7115652EA9C1696334F21D0C /* SSAlertViewExtention.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2280F2302F54E90122747821FE681543 /* SSAlertViewExtention.swift */; };
+ 592587436FBCCBC66012ABF6693E6516 /* RDEPUBParser+Archive.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1683830C2FBC2152212EA2219295035 /* RDEPUBParser+Archive.swift */; };
597DE4CDF5C674E269F03F6AA0AFFCC1 /* NSAttributedString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 92460D196C5300656C9649784EBD3C58 /* NSAttributedString+HTML.m */; };
- 5C7C59B5F46A9E7246C4E3276AA51579 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; };
- 5C9549182D7A2E372FC8DD289585ED92 /* DTHTMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FB2B1B0498DCBE7B79BDF49E963E3D /* DTHTMLParser.m */; };
5D03A5B9DC067875CFEC9D4EA7B059E8 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = D863AE4F659A0F71B22DC9FF334DDF4E /* DTCoreTextLayoutFrameAccessibilityElementGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5D4C9B19E8E778D12D7E7C5E6CD71E69 /* ConstraintMultiplierTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BE22EE711B7A38A4443E1C1C12BD79 /* ConstraintMultiplierTarget.swift */; };
5D8D65121E87E419D8D2F23F8AF43673 /* DTColor+Compatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 80B3D15ED0D19D020663EF055386291A /* DTColor+Compatibility.h */; settings = {ATTRIBUTES = (Public, ); }; };
5E90EBD876ED4E50AB890FB56C4BFA57 /* DTHTMLParserNode.h in Headers */ = {isa = PBXBuildFile; fileRef = C8343CDCF82729A8EBCCA37A6650E34F /* DTHTMLParserNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 5EB86ED8731B0325C112056B8209BE75 /* NSArray+DTError.h in Headers */ = {isa = PBXBuildFile; fileRef = CEA93EDA6ED54D374265AA5D6B9EBBA9 /* NSArray+DTError.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5EDFF78F62D56B5F1E93631C1404E969 /* DTSmartPagingScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 96EEB6B022E22A4ABC86D87469D4815B /* DTSmartPagingScrollView.m */; };
60532A2E06CCFF814933D2FAE6793AE9 /* DTCoreTextLayouter.h in Headers */ = {isa = PBXBuildFile; fileRef = C26DBE1150BC9A30B69839783E000358 /* DTCoreTextLayouter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 60B6352204C8B97A5A4E522C996CAF36 /* Archive+Progress.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD987C2FA7C8FBC96FD024E421DA0E68 /* Archive+Progress.swift */; };
+ 611999C9B1143944AD18014A385843B3 /* DTAnimatedGIF.h in Headers */ = {isa = PBXBuildFile; fileRef = A9FBC69C7DA29A7283920FDA9EBBCE8A /* DTAnimatedGIF.h */; settings = {ATTRIBUTES = (Public, ); }; };
61BE3607EABD2657BC71AF8ACB1C25D4 /* DTHTMLAttributedStringBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B59C9A1B265393E4BA8383B308A5917 /* DTHTMLAttributedStringBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 6212CD216FE19C8873AD9FFEC1A2E207 /* SSAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F410ED25E4E84C3D5166261CDF4B58F2 /* SSAlertView.swift */; };
- 648F9B76312C2127842C6E022BF52858 /* NSString+DTUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DCECDBD73595CA88A2208000B4B68352 /* NSString+DTUtilities.m */; };
- 66280717D1AFC25E8BA6B7B54DCE3C44 /* UIScreen+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = A3765B289CB7E6C5DF6160D8EBC83C3C /* UIScreen+DTFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 626DD93940B823CC844290213BD2FB5F /* LayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932FD688DE493323BA6B691BC2CB5094 /* LayoutConstraint.swift */; };
+ 64DBEC3A565BE464EAF89259C6E617F3 /* Typealiases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 945D4549E0B9168D7DDB910DE874B874 /* Typealiases.swift */; };
+ 650517717AE101F7FB41FFD30510F394 /* DTWeakSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = C422BCADBB48E28B549495663705A0FB /* DTWeakSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 659ABB2AB514F90759F10097FEA33BDD /* ConstraintRelation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3593ADF561D422F73C8EABAD4A4F440 /* ConstraintRelation.swift */; };
6634D568E49F5610242E1454680E5885 /* NSAttributedString+SmallCaps.h in Headers */ = {isa = PBXBuildFile; fileRef = DD189DA6054AAE2242F6644A2623B50E /* NSAttributedString+SmallCaps.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 67479B46544AEABAD34461B3B003241A /* NSString+DTFormatNumbers.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ACA5E519F100582F0D772FCD76125C0 /* NSString+DTFormatNumbers.m */; };
+ 668D823D051D368CF53AE35A8927C1A8 /* RDEPUBReadingSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1588FD9ABD6F9A35DD9047F1B0FC962B /* RDEPUBReadingSession.swift */; };
+ 676C79B07BED85F36B1AE9F5533E1BCC /* DTFoundation-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = FCC718115E5C31C88A680522AE36B089 /* DTFoundation-dummy.m */; };
67AE2AC8D9B961D45871077774E11FD3 /* DTObjectTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 073A7F9DF1A554BD1B324EA9E0090E91 /* DTObjectTextAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 6831E73FF77A7E5F85DA0722306FA75E /* DTExtendedFileAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 538572A5C098252D629F2185F1B1C9E8 /* DTExtendedFileAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 694E4526E9D04F90E1F1015AB8E76EFD /* RDEPUBWebView+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F40FA4C291CC3FC36B3FD11E0A65ED /* RDEPUBWebView+Search.swift */; };
69674FEA70899F07BE6098AAEC849978 /* DTAccessibilityViewProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 43CFADE0F46014990052AD67F17C86FF /* DTAccessibilityViewProxy.m */; };
- 6A5C6CE6A21014E06B14DBA211A753A2 /* RDReaderContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B42EEBDA6D4DC843F8681C93CA0477 /* RDReaderContentCell.swift */; };
- 6C10B69753EDACA90B7D80820969A739 /* RDReaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75928584ECA5C8361DC967CA3B3BB3FD /* RDReaderView.swift */; };
- 6C2AA05691D206D7D8AAE6DCD0DB7024 /* Debugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D495E2AB1223BD4D4909F8FF1FF47DAF /* Debugging.swift */; };
+ 6A2509D7D7ACB5D70EEC03C865E1F1CF /* RDEPUBWebView+Reflowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4C301C54B47F4B950AD3D60FBC3209 /* RDEPUBWebView+Reflowable.swift */; };
+ 6A3224C48B332465AE40A80572281FF0 /* RDEPUBStyleSheetBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 595F8E6BBA2C2F59D77807DBAC5569A5 /* RDEPUBStyleSheetBuilder.swift */; };
+ 6A7A2EFB24335CF2C4FE37A28AA6D0C3 /* RDEPUBTextContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EFDF760944B1E57CF1E42286AA9C7A8 /* RDEPUBTextContentView.swift */; };
+ 6AA01B56507A1A647CEB3E14087C9AEA /* ConstraintPriorityTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 882FA707ED3513D263A1E0656AAD2D33 /* ConstraintPriorityTarget.swift */; };
+ 6B42F46D76EEB4E5542B89EAE22CCE62 /* UIImage+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = D85A85D891A320165843EF9F489178F7 /* UIImage+DTFoundation.m */; };
+ 6B8330D7E54680D3CFB236F5DA3F3B12 /* NSString+DTUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DCECDBD73595CA88A2208000B4B68352 /* NSString+DTUtilities.m */; };
+ 6C0D068997665AA2FCD13AD8AAF44D80 /* ConstraintLayoutGuide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6BA0ECCB0ACF8580C3B5192CA9F11B /* ConstraintLayoutGuide.swift */; };
+ 6DF4F722488AF4D939035EBAEA822840 /* RDEPUBParser+Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A9D8548F1959F0F5B6804CF07F613A /* RDEPUBParser+Package.swift */; };
6E3D0FDD78ECC20F4B3697ED21D64224 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C2B218B5EAA5831A036BBF7E1EFC1B9 /* MediaPlayer.framework */; };
- 6E72E56686236F186CF662CD942CED77 /* DTLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B007AEEACE9EA271DD73AFD80A74D83 /* DTLog.m */; };
6FA0125F16DDEDAD341DB75DEB084F4B /* DTHTMLWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = E16D20FA492E085BD0871B107B3077B7 /* DTHTMLWriter.m */; };
+ 6FA0DD47E86052060301AE46D175CC3B /* Archive+ReadingDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934F9BB2B3A313294F121484C3A97A14 /* Archive+ReadingDeprecated.swift */; };
7027EF8D77D4082C1BB9B80FF61FCADC /* DTCoreTextLayoutFrame+Cursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 28043173CA3B3787DC401ACEB5398108 /* DTCoreTextLayoutFrame+Cursor.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 71E2DA4DD4ED8D9326FBBEB5A53B8DD7 /* Archive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C8F0DCEAA5A3DF38C503CA30DC94C1C /* Archive.swift */; };
+ 714C727B768A218E25EB277932A5C692 /* RDEPUBReaderChapterListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13B5042913A2E067B77876856E2152BF /* RDEPUBReaderChapterListController.swift */; };
+ 72AA7FCF0CD2A797EED6CB45BCCC468F /* DTBase64Coding.h in Headers */ = {isa = PBXBuildFile; fileRef = B8E40E302B85493BDE0FE91F829EC01E /* DTBase64Coding.h */; settings = {ATTRIBUTES = (Public, ); }; };
730ACB99580C5982DC08D50FC5084D41 /* DTAttributedTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = A276DE925253460239BFAEC7663F60D3 /* DTAttributedTextView.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 7420E7F0B49D30756ACB2D1E8E72A994 /* ConstraintDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02CF362F88AB69253F1898E53D7A286A /* ConstraintDSL.swift */; };
7459A1656EDC29D685073B83561B783E /* NSNumber+RomanNumerals.h in Headers */ = {isa = PBXBuildFile; fileRef = 8552BE1A1F6A6D2879763DE3B2B8A2DD /* NSNumber+RomanNumerals.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 760D93F76578312957931C9E591A25F0 /* UIApplication+DTNetworkActivity.h in Headers */ = {isa = PBXBuildFile; fileRef = 957DB4675D5CCBD3CB1762F08619F3F7 /* UIApplication+DTNetworkActivity.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 777EFABD70622E381088FAC153326C78 /* ConstraintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A02EA0374C693F881AB36202FB276DCD /* ConstraintView.swift */; };
7821105EF320619993B3BF7FB90C7ADA /* DTCSSStylesheet.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B610E558C4917C4257DC198EAFEE2F6 /* DTCSSStylesheet.h */; settings = {ATTRIBUTES = (Public, ); }; };
78D24D82D98D77E744FF73C9040618A8 /* DTHTMLParserNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 11B604FC3B0D5C91FEA763DCC0BE4DC5 /* DTHTMLParserNode.m */; };
7A48CDFB40202EFD09A2A9AB0EE0770D /* DTCoreTextFontDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 56489D9BA834497E48438A4B4E1CCCFF /* DTCoreTextFontDescriptor.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 7A725A8C6EA5F15EE13561418E2A4F8A /* RDEPUBPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349D1AF936EC0325D3F8D20BC3F7CBFA /* RDEPUBPreferences.swift */; };
- 7ADB53963D7B4B618A4333FB883D80A8 /* RDEPUBWebView+FixedLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A7D928BF63C11E27EE7F7E0BFB55DD /* RDEPUBWebView+FixedLayout.swift */; };
+ 7B5C38B63DF075846A3803C76172BFBF /* UIApplication+DTNetworkActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = 77213203802F44012346181FED2856F9 /* UIApplication+DTNetworkActivity.m */; };
7C05F56B62B15EFF344C5CDE4A52B8FB /* DTHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D4C9B7ED5E04C216AB28E3A4065C1C5 /* DTHTMLElement.m */; };
- 7C77E718ED1D925B023F654666E2FEC9 /* RDReaderContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08C90C5F0390F80AAB49159554B3CA98 /* RDReaderContentView.swift */; };
+ 7C69A3FC019CC9B5362CBA314E096675 /* RDEPUBReaderSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEB7EE3B285F7C4E8BABA0361010EC6F /* RDEPUBReaderSettings.swift */; };
+ 7D1F81899E05B6DA38FBF7CD06171F0B /* ConstraintLayoutSupportDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5B34EE2160C27947D1EEFD42184CF5D /* ConstraintLayoutSupportDSL.swift */; };
7D29C0A53AB1B4C1B257D46EBBDC9DA3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; };
- 7EEB624339403EEE4F78BDCC9ACD1620 /* Archive+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2659DB0685E16ABAE0382F726DA8C8 /* Archive+Helpers.swift */; };
+ 7DDACEAB770C2E7E0C0DF1C517318B05 /* RDEPUBReaderPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7CFC115966479905DEC4FC6AB841524 /* RDEPUBReaderPersistence.swift */; };
+ 7E64CD8BC758C61C77A77717D562AFD0 /* SSAlertPresentAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E481007A8174943868EBC81CF3B04B9F /* SSAlertPresentAnimation.swift */; };
+ 7F2CECE2875294C764A4B6740D920465 /* SSAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F410ED25E4E84C3D5166261CDF4B58F2 /* SSAlertView.swift */; };
7F9CC306B17D409DB903DF850FEDA64C /* DTListItemHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = F7B0087DE7C394A677F5883541AB4FB7 /* DTListItemHTMLElement.m */; };
- 7FFB4350996F7BBDDE7F06E6F33CC147 /* DTTiledLayerWithoutFade.m in Sources */ = {isa = PBXBuildFile; fileRef = 70F5D03B9B3B7F182C8F5C1F5D4FA7E7 /* DTTiledLayerWithoutFade.m */; };
- 80135317D71C52AD5130C0415C8444CF /* NSData+DTCrypto.m in Sources */ = {isa = PBXBuildFile; fileRef = 17F750F38183302BF345F40F5A4CDD43 /* NSData+DTCrypto.m */; };
+ 7FE934B56D10ED83CB38A877924F503D /* ConstraintDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02CF362F88AB69253F1898E53D7A286A /* ConstraintDSL.swift */; };
8099F1E613A15275EF5FC59ED3C73579 /* NSAttributedString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D3D7C8444F58D9B3FCB4F3006D2CFBA /* NSAttributedString+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 80BC5B79C78A01D26AC3B9935B0AEA57 /* ConstraintPriorityTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 882FA707ED3513D263A1E0656AAD2D33 /* ConstraintPriorityTarget.swift */; };
811D7CBCD710AE45FF80A4B05BFE444C /* UIFont+DTCoreText.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E1548FC42118F1A4164739DE6BE4E9 /* UIFont+DTCoreText.m */; };
814D0D63E5FD4AB7231E63064FE9EB5C /* NSAttributedString+DTDebug.m in Sources */ = {isa = PBXBuildFile; fileRef = BF4F3EC137168B92A5DA85AF50CD239F /* NSAttributedString+DTDebug.m */; };
81905A7717925A17E6B98E934A75B8BA /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0834E84632886CDD9911CAB76183F1CE /* QuartzCore.framework */; };
82119A0DC4981CC2AF389C7C4CC08186 /* DTListItemHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 98658510AA525496EBF5792E5D9884ED /* DTListItemHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 821BE4DCC293BC1D49F1179E312C0667 /* NSArray+DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = 73940D215D644B6815A09DA802CFBDDF /* NSArray+DTError.m */; };
- 822533963D00EA2D77A630C7B3526546 /* RDEPUBWebView+JavaScriptBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB543EA6526A50CEA213FD13D39A196 /* RDEPUBWebView+JavaScriptBridge.swift */; };
- 8318CFE7133DB1DAD7CF18125063F3C8 /* RDEPUBAssetRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40C0353B6131A70FB42BE9827C9CF39 /* RDEPUBAssetRepository.swift */; };
- 835D6F366AF9CD802C9E9CE467C4A678 /* UIView+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 79A77584AB3B92B2508AE0BCFCB8FD87 /* UIView+DTFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 830E9177D85A33C81CD4960E647668A5 /* ConstraintLayoutGuide+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A9A82CB094636AFB53D6D130D6219BA /* ConstraintLayoutGuide+Extensions.swift */; };
83A4BC9FE4765C9B848DD4925D99F362 /* DTAccessibilityElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 99299A9FD6EF739D18352AC5B9325CB0 /* DTAccessibilityElement.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 8524C67DDA10EE32ED32ED04CC82323C /* RDEPUBReaderSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B4C1DDC7008C253756D86F5A5EFF31 /* RDEPUBReaderSettingsViewController.swift */; };
- 864E624B7D5ED9342D9B27EEF7E2C0B1 /* RDEPUBNavigatorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7E1893259D2E01A812A5A423628EA7 /* RDEPUBNavigatorState.swift */; };
- 86819F92EAD044E7F66DD5469EDE6BCC /* Archive+MemoryFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174FC0CA05D20BB995E211DE5A0857DB /* Archive+MemoryFile.swift */; };
- 8816D987F0F85BF96ABA595963C28EDC /* DTTiledLayerWithoutFade.h in Headers */ = {isa = PBXBuildFile; fileRef = DF5EDCA3B21964D0462BE0982E2F0BF8 /* DTTiledLayerWithoutFade.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 89B17D6E98A1FEAD5B75ED13A92A2994 /* RDReaderTopToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9714F12B4844EDA09072E46460BD0BBC /* RDReaderTopToolView.swift */; };
+ 83E19A8A3EADAABE519D2E786A9F0DF3 /* RDReaderFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9FD5A46CAF695E34F708A0C30C3DE34 /* RDReaderFlowLayout.swift */; };
+ 840F646B3660ECB7B248E67F7F45424F /* DTLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A1EBB9497F4846C84CA0BF5E4F84FC7 /* DTLog.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 849C01A5BFA13219F09C39A54B6EED66 /* RDReaderCoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3407CEC2D9B906B36A90E8C7D8DBEE77 /* RDReaderCoverView.swift */; };
+ 8558300865988018B084D6A1D38AAF60 /* Data+Serialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC78D0B0F37BF9AD1058CACA9AAF9BE /* Data+Serialization.swift */; };
+ 88A11CB6C7D8F48C392C13F0A118433C /* RDEPUBParser+Resources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A77A22862EBE7AC618AD500E7DA7E1 /* RDEPUBParser+Resources.swift */; };
+ 89106EA658920D3AF4F0AB90CCDE5387 /* RDEPUBRenderRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E993B886F540EC4775478575199F34 /* RDEPUBRenderRequest.swift */; };
8A0A0FB23DB0021ADC9AD0D90631883A /* NSMutableString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6293E97E128E98559FB74D9A342EAD /* NSMutableString+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; };
8A35CC67F0AA6B26397C10D756EA34E8 /* DTTextHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = FB2ABB1D297442D91AA42F1407DB4A58 /* DTTextHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8AC6DB27F560159D4FE2E9122ECB16E7 /* RDReaderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE8B3F8B6F6630049800DB1307E37B7B /* RDReaderController.swift */; };
8AC90941E71199090BCED76DE89EFB60 /* DTAttributedTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 77A613841EB90827AF321DD50B21DB5D /* DTAttributedTextView.m */; };
+ 8ADE490B9C5809A8A8E2323D6A8B134F /* RDEPUBResourceURLSchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7B83494F3362CF10D37194F302D579 /* RDEPUBResourceURLSchemeHandler.swift */; };
8B405CE13E7D718A9340A82B52766AB2 /* NSCharacterSet+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 75FA8E843D9225BD65EB9D17825EC0F4 /* NSCharacterSet+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 8C49A1B81872149A3678BF69579219BA /* ConstraintItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28D62D3200AC3D4E42C81CDFF7E884E8 /* ConstraintItem.swift */; };
+ 8E3AEBAD9E3F0B13F6CDAE50A04D09B1 /* ConstraintDirectionalInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91144FC099DD458E61625CD939577DAA /* ConstraintDirectionalInsets.swift */; };
+ 8E72B50AA0FD0CC50CDCDE1DEF5C15AB /* Archive+WritingDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C7359A7C3FC7675FED220200A43A17 /* Archive+WritingDeprecated.swift */; };
8E8726381CBCFB1EDB350183456D4EF7 /* DTHTMLParserTextNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13AB6A32A293E3DC21A7047C6DB958CA /* DTHTMLParserTextNode.m */; };
- 90811D883793E1B2A86369910E783591 /* RDEPUBSearchModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BDF97DAA7A9F8075754F7800D54E4CE /* RDEPUBSearchModels.swift */; };
- 90A1F575B755060FBD576F1995105E9F /* ConstraintMultiplierTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BE22EE711B7A38A4443E1C1C12BD79 /* ConstraintMultiplierTarget.swift */; };
+ 8E92116C86CD440A0513A1CB3FF3703E /* UIScreen+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F051D813D50D20D53205D5FDCAA877C /* UIScreen+DTFoundation.m */; };
+ 8F9E8C79567A25E41F2572049AC23DA9 /* NSFileWrapper+DTCopying.h in Headers */ = {isa = PBXBuildFile; fileRef = 09C957F3199C596781EFA1DEA1DDCEC8 /* NSFileWrapper+DTCopying.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 9071D70E520BCE416243E3F7AC9D9045 /* DTExtendedFileAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = F5389D4E96A4AFE175691B4D3D4524E7 /* DTExtendedFileAttributes.m */; };
+ 908E936E415EDCA8CC7C29730AFDADE7 /* RDReaderCommon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E78734521FC2C49E81DEFAA400638CD /* RDReaderCommon.swift */; };
+ 90DF6AA52BAC987F391AC53EC26688AF /* RDEPUBJavaScriptBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = D160E62099265227D1A92BE1BB1D147E /* RDEPUBJavaScriptBridge.swift */; };
91FDA47810CE5CE2C48AED9205AE89B5 /* DTStylesheetHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = DA25968B7C9A40031DA45BB328EA8A2E /* DTStylesheetHTMLElement.m */; };
- 92AABD43EC0BCEBB7AD6A2E5D9360E64 /* DTCoreGraphicsUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = B6425B2235BA00FE865AF58EF44F1144 /* DTCoreGraphicsUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 9206C4A72D73F7A83EE763FD2732EA0E /* RDEPUBReaderToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F3B149D311344D829E4A72CDAE18122 /* RDEPUBReaderToolView.swift */; };
92C59F96D443525E8833676479FA60C5 /* DTVideoTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 73A99927E52B24583A6A2CF7E6F23848 /* DTVideoTextAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 9474F9FA18A1679F0728B1DCFDFB741B /* RDEPUBReaderTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEF62E423A9A3F98B1C4B95937EA6222 /* RDEPUBReaderTheme.swift */; };
- 948A2F537324BA51FD6ADD433D1BA185 /* SSAlertDefaultAnmation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81B8F42440B802E6EC2B1FF7E3022F3A /* SSAlertDefaultAnmation.swift */; };
+ 93BDA19886CA59538BFC83807A909690 /* NSDictionary+DTError.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC368BD2A8D266EB771E5FB9A5A5B3C /* NSDictionary+DTError.h */; settings = {ATTRIBUTES = (Public, ); }; };
94F5846979DBC90E2B646E975F5ECBA8 /* DTHTMLWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B96B0998B49D8E149C995474DA5FE5F /* DTHTMLWriter.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 94F895252B822A78E4E2C9D1F78E51DC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 1E89FE5F2996336E862E82C1E14E0EE7 /* PrivacyInfo.xcprivacy */; };
+ 96043C45610069120A81BB730BAD427E /* DTPieProgressIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DA1E8494B86FFD79C639DCB8C21E1BB /* DTPieProgressIndicator.m */; };
+ 961C590266D0B946C367C44D8858F3BD /* DTCustomColoredAccessory.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C6BE3B2DA9A8C4CD3760CDAD6F305A5 /* DTCustomColoredAccessory.m */; };
96507B8910C8C9DE0B221CFF0E8518D0 /* Pods-ReadViewDemo-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = EBA0272559B0F7FAD8AC670AC7431188 /* Pods-ReadViewDemo-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 9736A526993D81552AC527ADD422A680 /* DTFoundation-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = FCC718115E5C31C88A680522AE36B089 /* DTFoundation-dummy.m */; };
- 97F81E372B16CC0E49D9EB61DF8C9DD6 /* NSString+DTURLEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = CB7F3535507F56824BA21509302CF8B3 /* NSString+DTURLEncoding.m */; };
+ 977D9786669CEE5B44B7C7564F3C05E3 /* ConstraintDirectionalInsetTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = A651CEE8062C84A597DBC44ECA3DBCC1 /* ConstraintDirectionalInsetTarget.swift */; };
+ 9831F221324157AB14C926FFB3CECDF0 /* ConstraintMakerEditable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 392A75EAF280876545D14AA00517BA4C /* ConstraintMakerEditable.swift */; };
9854DC3F1763B6272C2EA3FC6EC40B95 /* NSAttributedStringRunDelegates.h in Headers */ = {isa = PBXBuildFile; fileRef = E20272295EC253826AEE1F83A15D47C5 /* NSAttributedStringRunDelegates.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 986746CF92B9FC2B4D80F358B6EBFC7E /* ConstraintMakerRelatable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFE19508E12BF8B0B52E8CB4F724217 /* ConstraintMakerRelatable+Extensions.swift */; };
987538B07E11741899878EA8BC947F51 /* NSCoder+DTCompatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 354C5DD0ACC742762200A640ADA859C6 /* NSCoder+DTCompatibility.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 98D826BB984EE3AB0EE1BA37E33CD569 /* DTFoundationConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = FD03567681A79FFB6E81B3105503065D /* DTFoundationConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
9917E9B804CD33AF3BE2A992A3510C97 /* NSNumber+RomanNumerals.m in Sources */ = {isa = PBXBuildFile; fileRef = B7672A26922DBD934FA1AC60D49EB322 /* NSNumber+RomanNumerals.m */; };
992B7C8C824B5EA190EF1DB096064DB7 /* DTDictationPlaceholderTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A2565B302334B662AB51DCF8E65EEE /* DTDictationPlaceholderTextAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 9A324EC84697C70962A7B098B8B03451 /* RDEPUBWebViewDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C176E5C0049EBD2438CAAA8D402F7FB7 /* RDEPUBWebViewDebug.swift */; };
+ 994077E940F308F9F22C96D55EEEB5BF /* RDReaderBottomToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6943D2E58182804A9C52172C4032491 /* RDReaderBottomToolView.swift */; };
+ 99B79FA72073A6349FB93A8881CD9E95 /* ConstraintPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45C7129757B54DFD9E445D0A17D73A83 /* ConstraintPriority.swift */; };
+ 9A8C333D2FCB63ABE673F41F869CC2F1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; };
+ 9ACA4CAA2EBF659F34B5DFD3053CAF68 /* RDPlainTextBookBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 899BBF53253C6175CDD9F3145E3354D8 /* RDPlainTextBookBuilder.swift */; };
+ 9AD9554A3038E42F561E57476BA3B650 /* NSString+DTPaths.h in Headers */ = {isa = PBXBuildFile; fileRef = FC5EC7C3D1808C9CF53354D7A3D69EE7 /* NSString+DTPaths.h */; settings = {ATTRIBUTES = (Public, ); }; };
9B199D71D2F48B2055A7FDF9BD7051CC /* NSString+CSS.m in Sources */ = {isa = PBXBuildFile; fileRef = E27C503C8128BC3CE109FFE077AEF325 /* NSString+CSS.m */; };
- 9B3AF2A7654B74B3FD1E5EBFDA1AF625 /* ConstraintLayoutGuideDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4684B406B325B35F39322D75B59B6479 /* ConstraintLayoutGuideDSL.swift */; };
+ 9B4B04971DFFB00CC4D0C18FF82EC198 /* ConstraintMaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60657D08CCDB38BFED30BD40F4720CE4 /* ConstraintMaker.swift */; };
+ 9BBED7A5BFD72153D310D7C9BE0AD43A /* DTAnimatedGIF.m in Sources */ = {isa = PBXBuildFile; fileRef = BC332335B380BC51C4F05C5CCDFB9189 /* DTAnimatedGIF.m */; };
+ 9BC445F0EC564500993D2017BE5AE93F /* SnapKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B10D3EC1701BBA4D69926915F942558 /* SnapKit-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
9BD146F72B29C703798001F138B069C2 /* NSCoder+DTCompatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 9129D37C7F89AD8FD530663BB4503FC0 /* NSCoder+DTCompatibility.m */; };
- 9C5C232A905EB12AE0BBF85B86F07B14 /* SSAlertAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97995C73297871C353364E685939DED9 /* SSAlertAnimationController.swift */; };
- 9C84E5A4EA3BB592E1D1BEFC89B829B9 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; };
- 9CC1744D5412A691E9CE5827D25FA026 /* Archive+Progress.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD987C2FA7C8FBC96FD024E421DA0E68 /* Archive+Progress.swift */; };
+ 9C4310ECF8069F153AB4DD79E2672EA0 /* RDEPUBPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559CDABFD5C6AC0A1D479DE506EBC216 /* RDEPUBPreferences.swift */; };
+ 9C765444347796E301243934FB55B270 /* NSDictionary+DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = C18480913E002315E3F5EC6945371697 /* NSDictionary+DTError.m */; };
+ 9D0A85694FB52BEE01B75B6E5153A663 /* RDEPUBPublication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C301D24F51B4532911A60156CB37626 /* RDEPUBPublication.swift */; };
+ 9D94B04BF6DB966245C560E87AFDFB9C /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEB14A8F8170777DB7CCC3F27C8EA57A /* ImageIO.framework */; };
9DE04C83A68C7512C0A860FFF5CEC113 /* DTCoreText.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F98F0F060B066CAFBCD3C85C26B2B4 /* DTCoreText.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 9E107AF056B2E380C59833C86F39DCB4 /* ConstraintAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9593E7B37569219F8D9A504B47707DC /* ConstraintAttributes.swift */; };
- 9E14B5E0563833C25C01BEFFB91A00F4 /* NSMutableArray+DTMoving.h in Headers */ = {isa = PBXBuildFile; fileRef = B0AC307F12521444C973EA0DE38B4608 /* NSMutableArray+DTMoving.h */; settings = {ATTRIBUTES = (Public, ); }; };
9E4AB71DE8B3E7218B53B2C8EF6B843A /* DTAttributedTextContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = F0D72C5680EBE6CA716EE77281AA4436 /* DTAttributedTextContentView.m */; };
+ 9EB49D91ADCD32CD6AA497FFD3FDD1C6 /* ConstraintInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C241AD9CEC9D6732CFBA6A55B49E64C9 /* ConstraintInsets.swift */; };
+ 9EE4E61E94498BA4D51D8A6692ACFAD4 /* RDEPUBReaderTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8CE1D225DE06A9108C93BC4A0786E03 /* RDEPUBReaderTheme.swift */; };
A132A0C9EB13B65FB886B11AA2E6304C /* NSDictionary+DTCoreText.h in Headers */ = {isa = PBXBuildFile; fileRef = 27205D159B39090908CA0E9C3997B784 /* NSDictionary+DTCoreText.h */; settings = {ATTRIBUTES = (Public, ); }; };
- A1838E746898D402076FFAFFF5710727 /* RDReaderTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7126988BFC0706CC32ADD178FD4BD05A /* RDReaderTheme.swift */; };
- A1CBB992340B83B99BECD87A49BC474B /* RDEPUBWebView+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55688D668F766F6CE3B4610F89DFA04 /* RDEPUBWebView+Search.swift */; };
- A1E2778CB7E0E2B5F3322F496B08F36E /* RDReaderBottomToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1118BEF683B4016DFEB612256A171DC /* RDReaderBottomToolView.swift */; };
- A2BB2FECE8CD6CA5B34712DBB6F79E78 /* RDEPUBReaderConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5A78F7BF534BAB3F7D8B18F95DA741C /* RDEPUBReaderConfiguration.swift */; };
+ A14A00A9209C6EA445C4A0DD0E878D83 /* epub-fixed-layout.html in Resources */ = {isa = PBXBuildFile; fileRef = B0E21F5874F5EFC61AC8DB384E7EDB66 /* epub-fixed-layout.html */; };
+ A1BD7384AF783ABFF25718FF6137B710 /* ConstraintItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28D62D3200AC3D4E42C81CDFF7E884E8 /* ConstraintItem.swift */; };
+ A28A32D4FE0F82F71862506258DF2B8C /* Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0EBAC7714A5F73B75AAB47D24B08B71 /* Constraint.swift */; };
A2CA9C27EB2B77637A12723F8CFE7AAB /* NSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 4428633C8EC3F6289CCF376F588CE0C4 /* NSString+HTML.m */; };
+ A2FD8600E9CBC6E568DB76AF3563A130 /* RDEPUBReaderTopToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10709A2725A69E31B57BB2FD9794062C /* RDEPUBReaderTopToolView.swift */; };
A30D5C1E413A3D319A83D3410A8A549F /* DTCoreTextParagraphStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F760D62F6C6F083374702146337F2CB /* DTCoreTextParagraphStyle.m */; };
- A419317B0CA6654DDF3819AEB214B7AA /* RDReaderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E90A625BAEFA03771A79C90309E8763 /* RDReaderManager.swift */; };
- A5D11822A56FE0A7181A32333A35C400 /* UIImage+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 15941683D394FB8D7EAE91CD2CF03FF1 /* UIImage+DTFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; };
- A6B18BB678B16F8B16BD2104A0A3F35D /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0834E84632886CDD9911CAB76183F1CE /* QuartzCore.framework */; };
+ A323A5A64651D2176D82B89ED1F03D2C /* DTCoreGraphicsUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = B6425B2235BA00FE865AF58EF44F1144 /* DTCoreGraphicsUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ A6CEE90E2ADCF94900CF1EFB13FD7E5B /* RDEPUBSearchModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3125023D51548B527DE43DDA360B494F /* RDEPUBSearchModels.swift */; };
A72E9F983E5BBFA7485EFB482B80E89E /* DTWeakSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 5155CCDD61E030184249A020FE5D74B0 /* DTWeakSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
- A7CE47E9D87549C2FFD656DCE734FB6C /* RDEPUBModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18ED210243FE63DA18C3696132AC3BEC /* RDEPUBModels.swift */; };
+ A7CC7CE89B80BD2DE5316B7F84DE7514 /* DTBlockFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 375179DD3A4576CEF947E1760BBDC69B /* DTBlockFunctions.m */; };
A7D665BBDADBE3D64023FF52E218FCFA /* DTCoreTextConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = B2B3D0CB47B0A8E9EC5AF48B7F2DD6B0 /* DTCoreTextConstants.m */; };
- A8FFA4BFFAABAD7610AD432855FF6294 /* DTActivityTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EEFAE80193E1F0DA53400C9AD641410 /* DTActivityTitleView.m */; };
+ A84CCAAAA79067FD6C6980228621EE73 /* RDEPUBTextRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 122439DD30580AA0349A6DD05D62A046 /* RDEPUBTextRenderer.swift */; };
+ A98D4EAFBF66ED88A44DBC43EBC3B2FE /* ConstraintMakerRelatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD768C27E392C934C06DFB4AC401037C /* ConstraintMakerRelatable.swift */; };
+ A99D0126BE153DF326CCF8A75BFCFF07 /* UIView+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4219E7C32019899890CAB66E5FBDF524 /* UIView+DTFoundation.m */; };
AA3722CC8B4992EDA8A9F5E9116835B7 /* DTCoreText-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 015F3EE69253C66E11B931E3360BA5FE /* DTCoreText-dummy.m */; };
+ AB9EEDEA66590F032C7D51559F3FE96E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; };
AC7000465258822B466855B12F1F97A3 /* DTCoreTextGlyphRun.h in Headers */ = {isa = PBXBuildFile; fileRef = 49A21AE1A0AB5383F895B34EA7D6973D /* DTCoreTextGlyphRun.h */; settings = {ATTRIBUTES = (Public, ); }; };
- ACA423D9FB3354CDDDBF643CE22CE830 /* RDReaderView-RDReaderViewAssets in Resources */ = {isa = PBXBuildFile; fileRef = 83410CC9CF2ABE63B90A92F2F988BF65 /* RDReaderView-RDReaderViewAssets */; };
- AE022C3439ECC9A9D40505857399D1DF /* Archive+Reading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0619F98DA80922E78A07321D0DC54F62 /* Archive+Reading.swift */; };
- AE5694EAD10C4414144D99F4A57FF985 /* RDEPUBParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DAD5578B00491EBE9F71E4C310BEAB2 /* RDEPUBParser.swift */; };
+ ACA9EC8442E9D645A940DB93881090CF /* DTActivityTitleView.h in Headers */ = {isa = PBXBuildFile; fileRef = 8272D0B59ABD6652D0CAE3DFCC2C5EE1 /* DTActivityTitleView.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ AD968151285A2287D21B5FFB7B53DFB2 /* RDReaderController+EPUBText.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9A9B435480AE1BDAD480B8B39F3CEB6 /* RDReaderController+EPUBText.swift */; };
AF7A1DC953992A149CB0815C90769529 /* DTIframeTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B931AB189BDDE2CCE8578B50BD60AF0 /* DTIframeTextAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; };
- AF82B3CE4B30C626876B85D711177FF1 /* DTBase64Coding.h in Headers */ = {isa = PBXBuildFile; fileRef = B8E40E302B85493BDE0FE91F829EC01E /* DTBase64Coding.h */; settings = {ATTRIBUTES = (Public, ); }; };
- AFC13A3280ED263A5D44A69B71C51D20 /* ZIPFoundation-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A06A36ADBE678DB44158D32E751FAFF /* ZIPFoundation-dummy.m */; };
- B17B23A2850DB58F7C21C4925DCB6040 /* Typealiases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 945D4549E0B9168D7DDB910DE874B874 /* Typealiases.swift */; };
- B2373FC0E50B236309843102F63735F8 /* URL+ZIP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537BB06317FA188DF7A8A960152520E8 /* URL+ZIP.swift */; };
- B2433BF10358BBF9204C53B2A9FE4989 /* NSURL+DTAppLinks.h in Headers */ = {isa = PBXBuildFile; fileRef = 557DA029E15EC99631B647AE8922007F /* NSURL+DTAppLinks.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ AF7A4F9912E7A6F28551DBAEA6F4C0F8 /* DTPieProgressIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B443D035BA5652B03A41BE905C5AC17 /* DTPieProgressIndicator.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ B03B160DB8C7DC5E01794737015AF909 /* default.css in Resources */ = {isa = PBXBuildFile; fileRef = 05D57D16346FAF830F1F82067307EA9F /* default.css */; };
+ B03E2E36A24D2475858DEA71F81501D5 /* ConstraintAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9593E7B37569219F8D9A504B47707DC /* ConstraintAttributes.swift */; };
+ B09F64CF66D958F377EC36059CDBE859 /* RDEPUBReaderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CA82B3E5B177B2456D28B7EB7D94D1F /* RDEPUBReaderDelegate.swift */; };
+ B17B4DAF45D7CE33259E75008B89F938 /* NSString+DTFormatNumbers.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ACA5E519F100582F0D772FCD76125C0 /* NSString+DTFormatNumbers.m */; };
+ B1FDBB21F27EB96BBC8E07F8BA888350 /* UIScreen+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = A3765B289CB7E6C5DF6160D8EBC83C3C /* UIScreen+DTFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ B1FFCA102E3E0CABC095784847C04C89 /* ConstraintMakerRelatable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFE19508E12BF8B0B52E8CB4F724217 /* ConstraintMakerRelatable+Extensions.swift */; };
B24FAD34A7915FC4B90FE9038E814FF2 /* DTLazyImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = A4E43DCDE0C8751557E7E65FEEB1C865 /* DTLazyImageView.m */; };
- B3375D8254C6AA7BF6A7EBA5EB93B52C /* NSString+DTURLEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 31BD7533867336DEA9B835F471767178 /* NSString+DTURLEncoding.h */; settings = {ATTRIBUTES = (Public, ); }; };
- B39B3765B48320BDB4C72BFD418D40E1 /* DTBlockFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 375179DD3A4576CEF947E1760BBDC69B /* DTBlockFunctions.m */; };
- B769CC652CB8A31A9CD85629CA38FF17 /* RDReaderCoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F8AAAE83A408855A21DDBA35672DE4 /* RDReaderCoverView.swift */; };
- B77BA265584D8F56E01218DCEEDB514D /* ConstraintView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DB34ACB132E465CDF8191F7DB71EF6 /* ConstraintView+Extensions.swift */; };
+ B40695AB9659A49AD6C9602F7F4FBFD6 /* RDEPUBDTCoreTextRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516C8F88FE57FE1637284CEADF7FCF26 /* RDEPUBDTCoreTextRenderer.swift */; };
+ B54A1DD9D08247520EB0F001A69D4F3A /* DTHTMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D75FEDE65722CF41F7F32A164376926 /* DTHTMLParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ B6DE5412EE98AF77C6361FABA372EC31 /* ZIPFoundation-ZIPFoundation_Privacy in Resources */ = {isa = PBXBuildFile; fileRef = CA4A114775B98CEA4A566C18123B145F /* ZIPFoundation-ZIPFoundation_Privacy */; };
B7BDD79FC06A1E4E0E17592860844108 /* NSAttributedString+DTCoreText.h in Headers */ = {isa = PBXBuildFile; fileRef = FF3F7BA20A1213804048557D946CEEA9 /* NSAttributedString+DTCoreText.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ B8140D14C3122E23ED54BDA6E0AFC3EF /* DTVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF6156128A1A0CA24D9D06E346B9445 /* DTVersion.m */; };
B8332F36582F7A001FE85A8D54B27C9E /* DTCoreTextParagraphStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D62C5531667F364F14DB67D33FFA554 /* DTCoreTextParagraphStyle.h */; settings = {ATTRIBUTES = (Public, ); }; };
B86493B996A888D1DA7814ECB6028E22 /* DTHorizontalRuleHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = A68D72764140BC3DF67A2B9E52CEC7A4 /* DTHorizontalRuleHTMLElement.m */; };
- B8D6D43C06F3439026A7F2D50328722E /* ConstraintLayoutSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50297AAB8278F2BB6FCFE82B20C7A049 /* ConstraintLayoutSupport.swift */; };
- B9105A4CE97DA800453EAE2AA5C01BD9 /* RDEPUBTextRendererSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC6905EAFC226D07EF263722C309854C /* RDEPUBTextRendererSupport.swift */; };
B94CD41727DFCA8791EED8FB2CA397DD /* DTCompatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 2702E05AA7A266B7D8D0F881FD4E09DA /* DTCompatibility.h */; settings = {ATTRIBUTES = (Public, ); }; };
- B94E1A40D4CFBBF1B15A2AD5E51726AB /* LayoutConstraintItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8C0D83EB6EDE71B264B6B0DD4644F5 /* LayoutConstraintItem.swift */; };
- B9C4C22D5F090F8F74D60454836AECC5 /* DTVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BD9F2023FF826F67E69E3407A70A8A6 /* DTVersion.h */; settings = {ATTRIBUTES = (Public, ); }; };
- BAA6B3FDE546695D4CB9CECDA4D22BDE /* DTActivityTitleView.h in Headers */ = {isa = PBXBuildFile; fileRef = 8272D0B59ABD6652D0CAE3DFCC2C5EE1 /* DTActivityTitleView.h */; settings = {ATTRIBUTES = (Public, ); }; };
- BB1CF40EA945D78A9FDF8C09DF39AF0E /* SSChapterListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78FBB07DBF1E01A21CC137A18701BD55 /* SSChapterListController.swift */; };
- BB35F0B1BFD910B09C7050ED75615253 /* DTCoreGraphicsUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = A8DFDCAB69B471EB5667C964865C26CF /* DTCoreGraphicsUtils.m */; };
- BB40243F36F73464C8FA8743DDC5F7E6 /* RDEPUBReaderChapterListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF09A1B1AD317B03B45C7B237599F836 /* RDEPUBReaderChapterListController.swift */; };
- BCE0CC51006B94B44F3CB80268781CDB /* RDPlainTextBookBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F69FCFB88B9F1663A813C2BF85531B8 /* RDPlainTextBookBuilder.swift */; };
- BD65F204331C89051D45AD6E3C1F8EEE /* Archive+BackingConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61E36C4FE448DDDA03450847FCB785B6 /* Archive+BackingConfiguration.swift */; };
+ B9588EC36B01C76FD4AD324A0A7F7421 /* Archive+BackingConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61E36C4FE448DDDA03450847FCB785B6 /* Archive+BackingConfiguration.swift */; };
+ BA66D8F48DC461E17CFDC6B50B686E20 /* DTBase64Coding.m in Sources */ = {isa = PBXBuildFile; fileRef = 061BD3911E4E530C792E65CED7868C33 /* DTBase64Coding.m */; };
+ BBEFD97865DA1E078DD7DE19BD85EE02 /* DTLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B007AEEACE9EA271DD73AFD80A74D83 /* DTLog.m */; };
+ BD4C931582665BED9A3C6D5E19A8DA69 /* RDEPUBAssetRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CF438DD2D09F503C1880E1386F7748B /* RDEPUBAssetRepository.swift */; };
+ BE0005C8530630D781B9AD68B19CF444 /* SSAlertCommonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE4EE750340347ED584C5A2C1164331A /* SSAlertCommonView.swift */; };
BE12CD112F82AD9050D7FB29395F27F9 /* DTVideoTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = 63A6EDF10DA5A051C6CDCBE5D97AF11E /* DTVideoTextAttachment.m */; };
- BE19D34FA74C191381772F5F4A168207 /* RDEPUBTextBookBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48AEE29A1D6F2A742E650107A79D306 /* RDEPUBTextBookBuilder.swift */; };
- BED0829591EE91BCFB2AF5F7923D2BCD /* DTVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF6156128A1A0CA24D9D06E346B9445 /* DTVersion.m */; };
- BF266DF1F85B1B4F88BB2039202C1294 /* SSAlertPresentAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E481007A8174943868EBC81CF3B04B9F /* SSAlertPresentAnimation.swift */; };
- C0CF89E5F97F846E5D486622B282E9C6 /* DTCustomColoredAccessory.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C6BE3B2DA9A8C4CD3760CDAD6F305A5 /* DTCustomColoredAccessory.m */; };
- C1ED9A831BE98CB9DDAB14784EB08278 /* NSMutableArray+DTMoving.m in Sources */ = {isa = PBXBuildFile; fileRef = 53499F40E1790A6CBE8AFEB3AC5BEFC8 /* NSMutableArray+DTMoving.m */; };
- C25F1A11C17B65702C02A04A88A20247 /* RDEPUBSearchEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCDC6024DBA244B7FC22AADE2459166E /* RDEPUBSearchEngine.swift */; };
- C4109FA44ACF1EDFE15292EC565611CE /* ConstraintInsetTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A5ABB8F3656A110B955B0306E9A8C3 /* ConstraintInsetTarget.swift */; };
- C470B0AC0A54613A91D77EF258A37ED1 /* RDEPUBReaderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E6E393B573F355F89671CA4F939665B /* RDEPUBReaderController.swift */; };
+ BE14B3B3AC808DE4A957528C004B5E0D /* RDEPUBParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75F2DC4A24F7A1558DD65BD6F43C270C /* RDEPUBParser.swift */; };
+ C1AF057FF8507D6AF368A0E7DCA6C0F3 /* ConstraintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A02EA0374C693F881AB36202FB276DCD /* ConstraintView.swift */; };
+ C1EDD8DEF419BFFA22FA1FF483000890 /* URL+ZIP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537BB06317FA188DF7A8A960152520E8 /* URL+ZIP.swift */; };
+ C255EE041A1D6EDF5DED4BA9CFA74D05 /* SSChapterListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445AD03473D9958284B5E463FD97C435 /* SSChapterListController.swift */; };
+ C3488B17C07C568B7FEE73839E0EA631 /* Data+Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5964AF1336E2F0DA0E2C32991E76C78C /* Data+Compression.swift */; };
+ C3EAEE1FCA53A2FE758A5F93F65E8B28 /* RDReaderContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E77630A7C7AD864C7EE2DE949AE69EFE /* RDReaderContentView.swift */; };
+ C4CBD1BE7B8120894BD59636D6BA2CE6 /* NSURL+DTAppLinks.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D36F7CDCD7D5788CDCB3A1421DB242B /* NSURL+DTAppLinks.m */; };
C5EE8E7373FBD24E66C678356393D871 /* DTCoreTextFontCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E791886E59DC0FAB02C6BBAB5BE5B6B /* DTCoreTextFontCollection.m */; };
C6B65C0EA759DF426FBBBB113BA3C3E8 /* DTBreakHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = F734214E067BDBDB581911E1A5CDAADB /* DTBreakHTMLElement.m */; };
C72CE4B974DFAE2C3037D425EB559314 /* DTCoreTextLayoutFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = CE6907AC04870132CCE3177E894607DD /* DTCoreTextLayoutFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
- C761BED7D57039035576EA7B24BC4575 /* ConstraintConstantTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F090A0289338B14050FAF16C22605B0 /* ConstraintConstantTarget.swift */; };
+ C7D1BFF3AA4C78DA6C2AA4F81AA9B758 /* Archive+ZIP64.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5693BD171B879406EA14C97F4AD8D8 /* Archive+ZIP64.swift */; };
C8F753B56A6C4D2A938B6EA68CF522F8 /* DTColorFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 86A0361390AA0C29BC1A5487083A12A7 /* DTColorFunctions.h */; settings = {ATTRIBUTES = (Public, ); }; };
- C9704E24DCB47A25FE152D0EFBDE1295 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; };
- C9815A44E65B98FB278BADD4B5A8D9A1 /* RDEPUBReaderTableOfContentsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFBC11B5F2560AE2791E846B8E1825DA /* RDEPUBReaderTableOfContentsItem.swift */; };
C9DD05EF2D27FFEBFA7BF6F437168501 /* DTCoreTextConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = C8C20D9A71E6605DEE80342E7122B683 /* DTCoreTextConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
- CA71E371F27AD7439E814FABF541DA6C /* Date+ZIP.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76A97683FB5E8E8B86EA4AD71F6F4B4 /* Date+ZIP.swift */; };
+ CC4412CC2896F364973B39AE2CF71EE9 /* RDEPUBReaderConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76F06BB8BEE4EA60085BA857565CC15 /* RDEPUBReaderConfiguration.swift */; };
CC8B8F3D758E98B51F69EDFAA69663A4 /* NSString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 8ABDEFEA94DC6408D92AC66DA3625DE5 /* NSString+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; };
- CD0C6CEB902047E1887091E97A6C319E /* ConstraintMakerEditable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 392A75EAF280876545D14AA00517BA4C /* ConstraintMakerEditable.swift */; };
- CD45E2C052DB56C02DE77F0E4662DDF9 /* ZIPFoundation-ZIPFoundation_Privacy in Resources */ = {isa = PBXBuildFile; fileRef = CA4A114775B98CEA4A566C18123B145F /* ZIPFoundation-ZIPFoundation_Privacy */; };
- CD70A361A26F1C5C781E24ED8AF7A122 /* RDEPUBTextPaginationSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D644BDD3105BC08DA9C544E19306C8 /* RDEPUBTextPaginationSupport.swift */; };
- CF40A28F87A5BC4ACF576FB6D0302E3A /* RDReaderEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2B0B74097B043FA6BF1CD38E6A891EC /* RDReaderEditView.swift */; };
- CF5BC04982D3D07CC7175969C7575769 /* RDEPUBTextSearchEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5F83842C5F5BAD9DC241FAB17E86C7 /* RDEPUBTextSearchEngine.swift */; };
- CFC0AA997B503CB60AFF6AC8201F4586 /* NSString+DTFormatNumbers.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D86C7063D1128EAEB3A30EBEF214B3F /* NSString+DTFormatNumbers.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ CCD53D0A93F8E08C9067983CFF3E25C6 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 1E89FE5F2996336E862E82C1E14E0EE7 /* PrivacyInfo.xcprivacy */; };
+ CE1D637DEF746F46702ACC5CBC7A3C70 /* RDEPUBModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C39C9E8CABC34AE9DF017E5AC2101E /* RDEPUBModels.swift */; };
+ CE26D03A630A0CE5613BB26FBCF60B75 /* RDEPUBNavigatorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D165714534D2B0ADBCF337C9F45F58C /* RDEPUBNavigatorState.swift */; };
CFC49467FE75BFABC22A1CE2FFA26570 /* DTImage+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 12A5EA26EE7539EDDA90649D22FB675D /* DTImage+HTML.m */; };
- D0986B23079051BDA7532464D0473082 /* RDEPUBParser+ReadingProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 802A3A8F8886D5ED2B798481AC345609 /* RDEPUBParser+ReadingProfile.swift */; };
+ D1272A538BF23F5A9924C9BF0413D5DA /* SnapKit-SnapKit_Privacy in Resources */ = {isa = PBXBuildFile; fileRef = B9DCB5EC0B1CDADD221717CADDF62359 /* SnapKit-SnapKit_Privacy */; };
+ D138DCA343B9286577119C76F1E85AC1 /* RDEPUBTextSearchEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 999A2E64244650202819CE18D4F3B0F2 /* RDEPUBTextSearchEngine.swift */; };
D1A93B2D7F1B312755C48790338D7712 /* DTIframeTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = AA3CFD777AD76F97EBA05B3920F5786A /* DTIframeTextAttachment.m */; };
- D224F8D623293752F9BA7FC0ED13BFF6 /* ConstraintOffsetTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F950CEF31AC0065C0BEAB26BA2CEFA1 /* ConstraintOffsetTarget.swift */; };
- D277060F424ACC82E91176E7557A9FAE /* RDEPUBParser+Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABE5E53B1B2443A2A1500944F08B6626 /* RDEPUBParser+Package.swift */; };
- D3BF939E3551B9778559579751EBA4BD /* SnapKit-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CE078B465FDEF40EC0469626A2C2469 /* SnapKit-dummy.m */; };
- D3FBB170DF7B65724BFC2A8803BD7FBE /* Archive+Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA251BAC8B430B3A4D4C4321046B990 /* Archive+Deprecated.swift */; };
+ D1A9EAF84E765E18A6CC6F9F4F226714 /* RDEPUBFixedLayoutTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DD417BD314029A21D99E7DB185D7E97 /* RDEPUBFixedLayoutTemplate.swift */; };
+ D1F83D27D002E85F40F78611BFFF5A82 /* RDEPUBReaderHighlightsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35A4374BE871FC7B88D2F8367054063F /* RDEPUBReaderHighlightsViewController.swift */; };
D526E03C098DA6CF8F01C1BC126C9BBA /* DTCoreText-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7768E097F8BDE9FBE3752B98CD0101 /* DTCoreText-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
- D6A0D593376EAE83DCFF2AE06DFCAC24 /* ConstraintViewDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E5637603691B9B35C2A886B686D3CE7 /* ConstraintViewDSL.swift */; };
- D733B419F33C21162E0BCC0368554693 /* RDEPUBJavaScriptBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = E99C1C8DDE364C51C9B4D0EA99D73CFD /* RDEPUBJavaScriptBridge.swift */; };
+ D538B4FDF79DEA634D4B4CDA2355B052 /* NSString+DTFormatNumbers.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D86C7063D1128EAEB3A30EBEF214B3F /* NSString+DTFormatNumbers.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D6763DD23C8BB6A060C5C7621EE2D813 /* RDEPUBTextPaginationSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1354C269365523C94513CE1DBCF3BE8 /* RDEPUBTextPaginationSupport.swift */; };
+ D776C48C467569DDE420638027463E9E /* Archive+Reading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0619F98DA80922E78A07321D0DC54F62 /* Archive+Reading.swift */; };
D82DDA4C1E5C4D5378A9E5EA3F8E9DFE /* DTBreakHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 533CFFF04BE7BC4BDEAE11DF675A84CA /* DTBreakHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; };
- D8CEF527DA04733563771919D042D29D /* ConstraintMakerFinalizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B32EA4B7A492EEF26CE260E9AEFA0B /* ConstraintMakerFinalizable.swift */; };
+ D871DB06452D8510C0F72AD32D2DEC7E /* RDEPUBPaginator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93A21D1CAE698EF56CD11CD4B0E037C4 /* RDEPUBPaginator.swift */; };
+ D88E10F16AD7354C13B9CCA6D5611C5F /* NSURL+DTUnshorten.m in Sources */ = {isa = PBXBuildFile; fileRef = C80065DBA5634DAA71129404975D7349 /* NSURL+DTUnshorten.m */; };
+ D8B6553F767022A13B62CF0D2CCA0FF5 /* NSData+DTCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 02EDFE0EEA47398F3E1A361A73AF6A6C /* NSData+DTCrypto.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D956EABE0E86DF4B929C4FBAEF302297 /* ConstraintConstantTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F090A0289338B14050FAF16C22605B0 /* ConstraintConstantTarget.swift */; };
+ DA341BCCD5A2FA2F248375A46F0820DB /* DTCoreGraphicsUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = A8DFDCAB69B471EB5667C964865C26CF /* DTCoreGraphicsUtils.m */; };
DA5B51E5D80C161D2902B6FC60DA5EC8 /* DTImageTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 426D442C01742E4361F5BC34D43152A6 /* DTImageTextAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DABF5D691ED76F10311CE157BDF782CD /* RDEPUBTextPaging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77D17BD78B36A019C8056CFC25E59881 /* RDEPUBTextPaging.swift */; };
DAD16D3E6EBB32FF96C63BFE6037065F /* DTLinkButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1B75050E71BDD96FC19EC6BA129E6A /* DTLinkButton.m */; };
- DAE25D750B6EE598E8EF2C5261A3A84F /* DTBlockFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = A0BD48DAF5AE9DB087F2552E99069709 /* DTBlockFunctions.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DAF7D141DA901D402B6BBA832D3187F6 /* RDReaderToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A332F7FFD586C1C582107D741EC1C58B /* RDReaderToolView.swift */; };
- DB5A62DDAC6CFEDDAE2B9367E35C1D82 /* RDEPUBParser+Resources.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAEC24FE0F4FDC0B36363FA80C505695 /* RDEPUBParser+Resources.swift */; };
- DBAC59F3E419C518F59628BD33DEBF7E /* NSURL+DTComparing.h in Headers */ = {isa = PBXBuildFile; fileRef = 731C0C61E3AD227D691FBA1C2C17BF7B /* NSURL+DTComparing.h */; settings = {ATTRIBUTES = (Public, ); }; };
DBCA42F7E1697F4EC82E0E7C9657851D /* DTAttributedTextCell.h in Headers */ = {isa = PBXBuildFile; fileRef = D4093D30B372960168C1349100DF7DE0 /* DTAttributedTextCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DC189E055D21CE98531D65F6D332B3CC /* RDEPUBPaginator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD4934DFCBEB55C064320D7F51924A4C /* RDEPUBPaginator.swift */; };
DC3E2932A82AD5D88D8B1EFBF004BEC8 /* DTCSSListStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = B509CD4B498635D4536245E43D802ED9 /* DTCSSListStyle.m */; };
+ DC9FC34D4467670B53513FF52494F1E0 /* ConstraintViewDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E5637603691B9B35C2A886B686D3CE7 /* ConstraintViewDSL.swift */; };
DD73CD0E5148961D1ABD80E050122314 /* DTCoreTextFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = A1B031E2B903C2F177195C1C3D1D1EDB /* DTCoreTextFunctions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DE16AB49F175AE3720D9E7CF333AF98A /* RDEPUBWebView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EB6323E931258E4D81A9E982166363 /* RDEPUBWebView+Configuration.swift */; };
DE68AD2F426FD462BC70F968E5AF5553 /* DTTextAttachmentHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ABC2F552388CD08EC1EFDEF606AE222 /* DTTextAttachmentHTMLElement.m */; };
+ DEC64CD590CBBFF9E535EFE43BA7FFDC /* RDEPUBResourceResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D5405B061A5FD838A4A27BEC3634AE /* RDEPUBResourceResolver.swift */; };
+ DF6E9E563A840E1773ADA634BE5FA6B5 /* Archive+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2659DB0685E16ABAE0382F726DA8C8 /* Archive+Helpers.swift */; };
DFB5A1C64C7908FD05BEFF7C7B571353 /* DTAttributedLabel.h in Headers */ = {isa = PBXBuildFile; fileRef = 5679D281C282896E6FC352C48E0AF439 /* DTAttributedLabel.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DFFD8F444CC92A236413A349CF6566FB /* RDReaderView-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E81B3AB171663B2CC4D33A8C94D65BF3 /* RDReaderView-dummy.m */; };
- E06B8A2AC7C56153698A022C92B30480 /* ConstraintConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7176B677A350927FC670368313DC2CA1 /* ConstraintConfig.swift */; };
- E13B8322281F9B12B35046CCC58F4321 /* DTLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A1EBB9497F4846C84CA0BF5E4F84FC7 /* DTLog.h */; settings = {ATTRIBUTES = (Public, ); }; };
- E14FD6AB85D3ABB887912CF25558B219 /* ConstraintPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45C7129757B54DFD9E445D0A17D73A83 /* ConstraintPriority.swift */; };
+ E00C258891EEA0578EA9EF53480BC2A5 /* RDURLReaderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F23E4422C91F1DFB7F0C23F0FC9A091 /* RDURLReaderController.swift */; };
E1DA58105A72D6C0B77A8A9F7822BD68 /* DTCoreTextLayoutFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 029130886CE8E422D16ADF97468CB1B0 /* DTCoreTextLayoutFrame.m */; };
- E209FD50FE810486C639F818BD17BD0F /* RDEPUBStyleSheetBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89537ECF9C470CDEBAABD38E17070806 /* RDEPUBStyleSheetBuilder.swift */; };
- E2F64C22EADDA42BB317B992CCA72403 /* RDEPUBReaderToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B867F4889E706AD274C03EBB4642382 /* RDEPUBReaderToolView.swift */; };
- E3517936DA66CEC067B033BF99D35922 /* NSURL+DTAppLinks.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D36F7CDCD7D5788CDCB3A1421DB242B /* NSURL+DTAppLinks.m */; };
+ E368277D0D26CA0D132606E9AA76FA44 /* DTVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BD9F2023FF826F67E69E3407A70A8A6 /* DTVersion.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ E37FBD2992898E0FFE6ED3DB9D818C40 /* SSAlertDefaultAnmation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81B8F42440B802E6EC2B1FF7E3022F3A /* SSAlertDefaultAnmation.swift */; };
E38FC7829B5BDD1588FA487E0FBAC208 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; };
- E52438CA848D75D836BA8CEA795EDDBA /* ConstraintMaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60657D08CCDB38BFED30BD40F4720CE4 /* ConstraintMaker.swift */; };
- E66806872242D14985335460412A4C96 /* NSString+DTPaths.m in Sources */ = {isa = PBXBuildFile; fileRef = A3565FEE19F19CB43A43409AFFF2BD2C /* NSString+DTPaths.m */; };
- E77D10383301B4CCCDE02820A4B0CD9B /* ConstraintMakerExtendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5245010774F8F1C4857763CC7B96E7B4 /* ConstraintMakerExtendable.swift */; };
- E7DA07EB8A7D70FB28B81DB63630C801 /* DTWeakSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = C422BCADBB48E28B549495663705A0FB /* DTWeakSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
- E82A48A3C8A8410201F6F9E852A0DC05 /* DTCompatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = B5FCE04EBB553439D1C2BA1001936293 /* DTCompatibility.h */; settings = {ATTRIBUTES = (Public, ); }; };
- E83F5744609732E5221C0C263A800468 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 53D473C6F07982D3D25781A343EC5A99 /* PrivacyInfo.xcprivacy */; };
+ E39CBC60818A2C12DA7FC4C1BB3A286A /* LayoutConstraintItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8C0D83EB6EDE71B264B6B0DD4644F5 /* LayoutConstraintItem.swift */; };
+ E680926B8019191C653B7CAA2E4A6F1D /* NSArray+DTError.h in Headers */ = {isa = PBXBuildFile; fileRef = CEA93EDA6ED54D374265AA5D6B9EBBA9 /* NSArray+DTError.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ E754745F6357D98C0EC987EC7012560C /* UIApplication+DTNetworkActivity.h in Headers */ = {isa = PBXBuildFile; fileRef = 957DB4675D5CCBD3CB1762F08619F3F7 /* UIApplication+DTNetworkActivity.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ E85BC640958A6F4E356876A05AFF8D7F /* SnapKit-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CE078B465FDEF40EC0469626A2C2469 /* SnapKit-dummy.m */; };
E8AB5CE26A5224EF09E9E363D70C7775 /* DTDictationPlaceholderView.m in Sources */ = {isa = PBXBuildFile; fileRef = AFE0B56638AFB77F4455784A7ED59876 /* DTDictationPlaceholderView.m */; };
- E9DAC874AD0EAB7B1FA96B82A5533F3B /* NSFileWrapper+DTCopying.m in Sources */ = {isa = PBXBuildFile; fileRef = 1756C6FFCA695290BA62DA4E7B6DD20B /* NSFileWrapper+DTCopying.m */; };
+ E8CA146A59C3D83E974E1EDDFF282408 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; };
+ E93276599C08B7AA37AD2DBC93ADE01B /* NSMutableArray+DTMoving.m in Sources */ = {isa = PBXBuildFile; fileRef = 53499F40E1790A6CBE8AFEB3AC5BEFC8 /* NSMutableArray+DTMoving.m */; };
EA0040823CCF8E97363DB5891F5DA6D0 /* DTCoreTextFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F2D44B82815CA3397BE2C9F64819B88 /* DTCoreTextFunctions.m */; };
- EAAC1646A1A54CB6A24CCEB637F89F18 /* FileManager+ZIPDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F9B298989305B9569B71E448E8C1D4 /* FileManager+ZIPDeprecated.swift */; };
- EB4F82CB6B7194273C86D1474AE1BC42 /* RDEPUBTextRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCA9AFA3BFA7B321228218BEB960BC8 /* RDEPUBTextRenderer.swift */; };
+ EAF8B55B1B0522A985D2295B28A067A9 /* ConstraintLayoutSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50297AAB8278F2BB6FCFE82B20C7A049 /* ConstraintLayoutSupport.swift */; };
EBB7E4D4DB253B5F1267CF9B4DDB4E93 /* DTCoreTextFontDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCA590DB72AA93275262A414F16578A /* DTCoreTextFontDescriptor.m */; };
- EC4A32F257D9D3BB3752A82F6CAC5FC4 /* RDEPUBParser+TOC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F9C7B828F676C6EA37BDFC29FE9047 /* RDEPUBParser+TOC.swift */; };
- ED00387B997C6DDF8A7BF2881746B410 /* Data+CompressionDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D641809A99849F80FDCB02F8CE8D6A0 /* Data+CompressionDeprecated.swift */; };
+ EC83A916E8A3AD29E7A3204FA58EE64C /* epub-bridge.js in Resources */ = {isa = PBXBuildFile; fileRef = 0B23ADEA10EDE30BA85BC6061856A5F4 /* epub-bridge.js */; };
+ ED67F915411D2E2132519D5CF389BEF4 /* Archive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C8F0DCEAA5A3DF38C503CA30DC94C1C /* Archive.swift */; };
+ ED7CFFE11CA367F4BCACAA9FEB7CAF58 /* ConstraintMakerPrioritizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E138EC76D0DF24544D29D1A4D7429E3 /* ConstraintMakerPrioritizable.swift */; };
EDA269B595D70EC2B9D81DC890157D63 /* DTAccessibilityViewProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = F37563A56C180CE18EEFADE56FA3C17F /* DTAccessibilityViewProxy.h */; settings = {ATTRIBUTES = (Public, ); }; };
- EDA429603F6CB74D80FD015A3436CC38 /* RDEPUBNavigatorLayoutContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 197AC6C8795B18D61852C4A3E063F8BC /* RDEPUBNavigatorLayoutContext.swift */; };
- EDCC2A6AC402E55A3ABBDABBD3F6C69C /* RDEPUBPublication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23BF5DD0E252AFC65995DDB96AE61B9B /* RDEPUBPublication.swift */; };
+ EDD77256B4A5A92D757B813771C74921 /* DTFolderMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = B245F7E143F3091652C64F6593E33F7A /* DTFolderMonitor.m */; };
+ EE1DEAE7084BFE67E59435469B15C735 /* ConstraintView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DB34ACB132E465CDF8191F7DB71EF6 /* ConstraintView+Extensions.swift */; };
EE4F0992A0F597320FF7BDE4691D0FD4 /* NSString+CSS.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D6C48939A8498A09DCFC7F41ECB8A25 /* NSString+CSS.h */; settings = {ATTRIBUTES = (Public, ); }; };
- EEC826E7D681982C3C7927C6EC44C916 /* UIApplication+DTNetworkActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = 77213203802F44012346181FED2856F9 /* UIApplication+DTNetworkActivity.m */; };
- EF022D08A4D0A2643B8EFCDFCA46927E /* epub-bridge.js in Resources */ = {isa = PBXBuildFile; fileRef = A9A150D0B3A437949B3736DCE25DEA2E /* epub-bridge.js */; };
- EF6C9C088F2BCD53F9215D418C73F290 /* Archive+ReadingDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934F9BB2B3A313294F121484C3A97A14 /* Archive+ReadingDeprecated.swift */; };
+ EF1F9FC5601C62E0BFB57EBA6D14095D /* RDEPUBTextLayouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E3445FB098B54755E3CB3C81A9D349 /* RDEPUBTextLayouter.swift */; };
F006F73F17F2E3CC0E7852E9357BA946 /* DTAnchorHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 424F12BEB0EDAAF1C719B5103D30C653 /* DTAnchorHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; };
F0F631056BADA32253528247DF493272 /* DTHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = EE9D4A455723797A27F04A15C4E47823 /* DTHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; };
- F11862B7E3A020E71C36A5563FBA4CA2 /* RDReaderController+EPUBText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B01390913A39CC12CB719F529BC1EE /* RDReaderController+EPUBText.swift */; };
- F16C572F80328B2CF73623D67E671E9B /* NSFileWrapper+DTCopying.h in Headers */ = {isa = PBXBuildFile; fileRef = 09C957F3199C596781EFA1DEA1DDCEC8 /* NSFileWrapper+DTCopying.h */; settings = {ATTRIBUTES = (Public, ); }; };
F1E9B6D0728711AE4D7154616DBC17EC /* Pods-ReadViewDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = AA0811310099A4BCB3D52683B70DB3D1 /* Pods-ReadViewDemo-dummy.m */; };
- F28FABC3C68BB6E9D5A81FB6AB6A7A79 /* DTBase64Coding.m in Sources */ = {isa = PBXBuildFile; fileRef = 061BD3911E4E530C792E65CED7868C33 /* DTBase64Coding.m */; };
- F32219099BEEEE06C89A9C8492047B94 /* RDEPUBReaderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 415C064D271E9E8356C242E3043DB6F2 /* RDEPUBReaderDelegate.swift */; };
- F35E8550AFE0664263FCB95E0BE1324F /* RDReaderEPUBContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41E1A4E33EBA7B1301F94F8125D09C19 /* RDReaderEPUBContentView.swift */; };
+ F20405DA00A3C5CC585FB7D8E2B464D5 /* Archive+MemoryFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174FC0CA05D20BB995E211DE5A0857DB /* Archive+MemoryFile.swift */; };
+ F298A08262481DF3553CB4A723F79457 /* DTCompatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = B5FCE04EBB553439D1C2BA1001936293 /* DTCompatibility.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F2BE5795F53376085B3AA360C970CACB /* UIView+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 79A77584AB3B92B2508AE0BCFCB8FD87 /* UIView+DTFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; };
F4A2E3C4237B8931F61B72FF0BDD6D69 /* DTObjectTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = F038FB491B9A4A53447EF0DC333301AE /* DTObjectTextAttachment.m */; };
- F4CCF2839E53B60C2A8FBFE884A78919 /* FileManager+ZIP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27B7D3C6F0C845E3D42BBA613FFB0465 /* FileManager+ZIP.swift */; };
F58ABA106C6200175DF574DF05A75BA8 /* DTAccessibilityElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F0486D3B46DC92AB254D6EE9CC233BF /* DTAccessibilityElement.m */; };
F5A424E67F41C4F7B830CE2F7E71570D /* DTCoreText-Resources in Resources */ = {isa = PBXBuildFile; fileRef = F4E2B1AC73977F10254628C0B7EBE38F /* DTCoreText-Resources */; };
- F5B4AE13F77C7B6ABB8E89EF3E2A047B /* DTFoundation-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 5FAB3C7F19A511C2B1B7DFBA3CA308C4 /* DTFoundation-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
F6036BBDFE7484F5A453F6876C97C098 /* NSAttributedString+SmallCaps.m in Sources */ = {isa = PBXBuildFile; fileRef = 76F99C8130EE83A22CD223C7F5E6E66E /* NSAttributedString+SmallCaps.m */; };
F61102C9BF3D6296CB6C6B754ABF98C5 /* DTTextHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CFEE298DD52246B46E6FA62B16B38DF /* DTTextHTMLElement.m */; };
- F71ABC84C473CDC61260CBF6DDAA4224 /* DTAnimatedGIF.m in Sources */ = {isa = PBXBuildFile; fileRef = BC332335B380BC51C4F05C5CCDFB9189 /* DTAnimatedGIF.m */; };
- F7B6F97C5352C3BDD7CD96D0CFE7D115 /* ConstraintMakerPrioritizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E138EC76D0DF24544D29D1A4D7429E3 /* ConstraintMakerPrioritizable.swift */; };
- F9723AA08151437C7255DED18D120196 /* default.css in Resources */ = {isa = PBXBuildFile; fileRef = 05D57D16346FAF830F1F82067307EA9F /* default.css */; };
- F9CAB58F64167EBB8DE64F1A98F44EFF /* ConstraintDirectionalInsetTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = A651CEE8062C84A597DBC44ECA3DBCC1 /* ConstraintDirectionalInsetTarget.swift */; };
- FA20C44D3C33A28C62B34984670FAB34 /* ConstraintLayoutGuide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6BA0ECCB0ACF8580C3B5192CA9F11B /* ConstraintLayoutGuide.swift */; };
- FAD8FBB11524B7249563E94D85CCF849 /* epub-fixed-layout.html in Resources */ = {isa = PBXBuildFile; fileRef = C1F431965DA9137D064CF84B62EE4F49 /* epub-fixed-layout.html */; };
- FBE1772BA17AA227541140AF4EAA87FD /* RDEPUBFixedLayoutTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E30451C7CFBBBA73D81F8A206611499 /* RDEPUBFixedLayoutTemplate.swift */; };
+ F79FCC120C332CAE849758D835BE8AAC /* UIImage+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 15941683D394FB8D7EAE91CD2CF03FF1 /* UIImage+DTFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F8A022406FFE8AE5041E7C88C88C881A /* DTFoundationConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = FD03567681A79FFB6E81B3105503065D /* DTFoundationConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ FAA6C007FEB1EC8C3EA261A1C414CD15 /* Archive+Writing.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF988440049800DB7824A94B81D20254 /* Archive+Writing.swift */; };
+ FAE7A8F022854AD29FC640815AF6B8DC /* Data+CompressionDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D641809A99849F80FDCB02F8CE8D6A0 /* Data+CompressionDeprecated.swift */; };
+ FB43893F1DC71AFBDE690EE2F1CEC9B8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; };
+ FBD1C622985A6F78FFBA29EBC1EF79E5 /* SSAlertSwift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DE4FA5133891593CB45AF36B8EBF60C /* SSAlertSwift-dummy.m */; };
FC5F52919451A15639442B46B38E4C67 /* DTCoreTextLayoutFrame+Cursor.m in Sources */ = {isa = PBXBuildFile; fileRef = E62C5E593037EF8A45E7FAA847D0277C /* DTCoreTextLayoutFrame+Cursor.m */; };
FD5D68887297CFA94F543C49DC68A156 /* DTHTMLAttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 7C496AB6A73C2D0BD0FF340A6A2960D5 /* DTHTMLAttributedStringBuilder.m */; };
- FD5F762B5E46EAFDB164FDDFE4E4D5DC /* ConstraintInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C241AD9CEC9D6732CFBA6A55B49E64C9 /* ConstraintInsets.swift */; };
- FD6C5E615EC0DF087016519B54FB63B8 /* DTPieProgressIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B443D035BA5652B03A41BE905C5AC17 /* DTPieProgressIndicator.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ FDD911626EEF6C7B0DB2ABCBD0D86269 /* UIViewFrameExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C351514678D41C58869BD057D5483F /* UIViewFrameExtension.swift */; };
FEB00C614DCB6A8A7BDF8DEE11260984 /* NSMutableAttributedString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = B0500CA69405E23B09291359EB28331E /* NSMutableAttributedString+HTML.m */; };
- FFD86EE5E8007690C55D52BB15AA3C35 /* NSURL+DTUnshorten.h in Headers */ = {isa = PBXBuildFile; fileRef = 054674EB90C625BB4BEA9368ED92592B /* NSURL+DTUnshorten.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ FF4D6B037F3C8BB311D307F80BF37160 /* RDEPUBWebContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B29E51C9DCE79B5410B6D775EEAE81EB /* RDEPUBWebContentView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
- 0113E4A61D4C3C76E32F1029F6BF224C /* PBXContainerItemProxy */ = {
+ 0375A0AE82C989D2EADFC6DA0D51C741 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = 19622742EBA51E823D6DAE3F8CDBFAD4;
- remoteInfo = SnapKit;
+ remoteGlobalIDString = AA15C8469D67684160CC2A7098EB841C;
+ remoteInfo = ZIPFoundation;
};
- 0F3AC045A36C8217142C4FB810BC06D5 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = C7A8D82E407CD3FDC3BA55CEE519B252;
- remoteInfo = "ZIPFoundation-ZIPFoundation_Privacy";
- };
- 425790F38E14C4275839332B1B8FA902 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = B88F4EA0695B6B3165C64594850D72C7;
- remoteInfo = DTCoreText;
- };
- 816FD0C72170A9B81DD3BD37FC29C177 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = 8F6E5A5BF72D62CDFD25F91A7CFA3309;
- remoteInfo = DTFoundation;
- };
- 8FBDC29DE7B120EC491FBE414F48BEBE /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = 0C24CB0E87A728A11AA1124CB360D6A1;
- remoteInfo = "DTCoreText-Resources";
- };
- 9EC89934DF1BC378209D68D4998543E6 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = 8619D5ADECF2B26CFD9A9826D61D289A;
- remoteInfo = SSAlertSwift;
- };
- AB8235BC287EDAE64000A41D0A2FA825 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = 19622742EBA51E823D6DAE3F8CDBFAD4;
- remoteInfo = SnapKit;
- };
- ABEC31254A4EF8DE375162752DAD97B9 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = AA2E57587AA8EECA63C4BE08EA3CB6D2;
- remoteInfo = RDReaderView;
- };
- B4B5695CC90BFBBA842FD99151850594 /* PBXContainerItemProxy */ = {
+ 183E522DC6EE5169C3284B80F90705A2 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
proxyType = 1;
remoteGlobalIDString = AE7F393FB7805DE2664AB4111873F907;
remoteInfo = "RDReaderView-RDReaderViewAssets";
};
- BD0A503BA044661AEFB9A512A60F47F5 /* PBXContainerItemProxy */ = {
+ 2F02355C6737019E92BAD831A2F04C87 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
proxyType = 1;
remoteGlobalIDString = AA15C8469D67684160CC2A7098EB841C;
remoteInfo = ZIPFoundation;
};
- C45E8434CEB9FBED154179AAC32DFEC0 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = B88F4EA0695B6B3165C64594850D72C7;
- remoteInfo = DTCoreText;
- };
- D7C98D5FF28045F18FCCE4621CF64D9A /* PBXContainerItemProxy */ = {
+ 44B7687B4B1475A062EC94D1F1E875C1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 8A8DB685241263AFDF5E6B20FE67B93A;
remoteInfo = "SnapKit-SnapKit_Privacy";
};
- D927F18FDE4A2C1387F2979C91A6C6F8 /* PBXContainerItemProxy */ = {
+ 58013EE95C35DBCF692979CC8B613DE6 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = B88F4EA0695B6B3165C64594850D72C7;
+ remoteInfo = DTCoreText;
+ };
+ 665D1816F419A7294D5A04CFC4253F3E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 19622742EBA51E823D6DAE3F8CDBFAD4;
+ remoteInfo = SnapKit;
+ };
+ 796B88BABD6A76940F49528F9B0D0077 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 8619D5ADECF2B26CFD9A9826D61D289A;
remoteInfo = SSAlertSwift;
};
- DAFE0239226EFE279939BFEA8D3D0DF8 /* PBXContainerItemProxy */ = {
+ 79B06AE529229774DE8397539AD4B925 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = AA2E57587AA8EECA63C4BE08EA3CB6D2;
+ remoteInfo = RDReaderView;
+ };
+ 7A788A1351442DFC23621DE532FF8BE0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 8F6E5A5BF72D62CDFD25F91A7CFA3309;
remoteInfo = DTFoundation;
};
- E3F073D8BFE89C8B501C6B092A5CBA8D /* PBXContainerItemProxy */ = {
+ 8F6B3564342A77570514B41AA400A69A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = AA15C8469D67684160CC2A7098EB841C;
- remoteInfo = ZIPFoundation;
+ remoteGlobalIDString = 8619D5ADECF2B26CFD9A9826D61D289A;
+ remoteInfo = SSAlertSwift;
+ };
+ A3F19C80AC684CB56A166518A010138E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 19622742EBA51E823D6DAE3F8CDBFAD4;
+ remoteInfo = SnapKit;
+ };
+ D26FF136D573687578CA96B17C200174 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = C7A8D82E407CD3FDC3BA55CEE519B252;
+ remoteInfo = "ZIPFoundation-ZIPFoundation_Privacy";
+ };
+ D39C58A9BDB11D783F9EACD75B90F890 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 0C24CB0E87A728A11AA1124CB360D6A1;
+ remoteInfo = "DTCoreText-Resources";
+ };
+ F12B2A44C2679617137FFD2CF1F07C25 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = B88F4EA0695B6B3165C64594850D72C7;
+ remoteInfo = DTCoreText;
+ };
+ F2AB3A8588B4A770E1590191F52C4F66 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 8F6E5A5BF72D62CDFD25F91A7CFA3309;
+ remoteInfo = DTFoundation;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
+ 00EB6323E931258E4D81A9E982166363 /* RDEPUBWebView+Configuration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBWebView+Configuration.swift"; sourceTree = ""; };
015F3EE69253C66E11B931E3360BA5FE /* DTCoreText-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "DTCoreText-dummy.m"; sourceTree = ""; };
029130886CE8E422D16ADF97468CB1B0 /* DTCoreTextLayoutFrame.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextLayoutFrame.m; path = Core/Source/DTCoreTextLayoutFrame.m; sourceTree = ""; };
02CF362F88AB69253F1898E53D7A286A /* ConstraintDSL.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintDSL.swift; path = Sources/ConstraintDSL.swift; sourceTree = ""; };
02EDFE0EEA47398F3E1A361A73AF6A6C /* NSData+DTCrypto.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSData+DTCrypto.h"; path = "Core/Source/NSData+DTCrypto.h"; sourceTree = ""; };
- 031EBE9DD66645BDA304759CCB59C833 /* RDEPUBResourceURLSchemeHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBResourceURLSchemeHandler.swift; sourceTree = ""; };
- 03541C9357524E19B174851FCAEA660E /* RDEPUBWebView+Configuration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBWebView+Configuration.swift"; sourceTree = ""; };
045ADBF2087ADD914A9CA0B62EBF2A9F /* Pods-ReadViewDemo.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-ReadViewDemo.modulemap"; sourceTree = ""; };
04F9B298989305B9569B71E448E8C1D4 /* FileManager+ZIPDeprecated.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "FileManager+ZIPDeprecated.swift"; path = "Sources/ZIPFoundation/FileManager+ZIPDeprecated.swift"; sourceTree = ""; };
054674EB90C625BB4BEA9368ED92592B /* NSURL+DTUnshorten.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSURL+DTUnshorten.h"; path = "Core/Source/NSURL+DTUnshorten.h"; sourceTree = ""; };
@@ -499,41 +500,43 @@
061BD3911E4E530C792E65CED7868C33 /* DTBase64Coding.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTBase64Coding.m; path = Core/Source/DTBase64Coding.m; sourceTree = ""; };
073A7F9DF1A554BD1B324EA9E0090E91 /* DTObjectTextAttachment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTObjectTextAttachment.h; path = Core/Source/DTObjectTextAttachment.h; sourceTree = ""; };
0834E84632886CDD9911CAB76183F1CE /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; };
- 08C90C5F0390F80AAB49159554B3CA98 /* RDReaderContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderContentView.swift; sourceTree = ""; };
09BE22EE711B7A38A4443E1C1C12BD79 /* ConstraintMultiplierTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMultiplierTarget.swift; path = Sources/ConstraintMultiplierTarget.swift; sourceTree = ""; };
09C2D4A3D4F602D125FF38B56C91CF84 /* ZIPFoundation.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = ZIPFoundation.modulemap; sourceTree = ""; };
09C957F3199C596781EFA1DEA1DDCEC8 /* NSFileWrapper+DTCopying.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSFileWrapper+DTCopying.h"; path = "Core/Source/NSFileWrapper+DTCopying.h"; sourceTree = ""; };
0B10D3EC1701BBA4D69926915F942558 /* SnapKit-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SnapKit-umbrella.h"; sourceTree = ""; };
+ 0B23ADEA10EDE30BA85BC6061856A5F4 /* epub-bridge.js */ = {isa = PBXFileReference; includeInIndex = 1; name = "epub-bridge.js"; path = "Sources/RDReaderView/Resources/epub-bridge.js"; sourceTree = ""; };
0B7771FDE9AB1688166584C0AA03F12A /* DTCoreTextFontCollection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextFontCollection.h; path = Core/Source/DTCoreTextFontCollection.h; sourceTree = ""; };
- 0BCBA99577F4C792AAF4111AD4D40010 /* RDEPUBReaderTopToolView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderTopToolView.swift; sourceTree = ""; };
0C6BE3B2DA9A8C4CD3760CDAD6F305A5 /* DTCustomColoredAccessory.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCustomColoredAccessory.m; path = Core/Source/iOS/DTCustomColoredAccessory.m; sourceTree = ""; };
0D75FEDE65722CF41F7F32A164376926 /* DTHTMLParser.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTHTMLParser.h; path = Core/Source/DTHTMLParser/DTHTMLParser.h; sourceTree = ""; };
0E138EC76D0DF24544D29D1A4D7429E3 /* ConstraintMakerPrioritizable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMakerPrioritizable.swift; path = Sources/ConstraintMakerPrioritizable.swift; sourceTree = ""; };
0E8C0D83EB6EDE71B264B6B0DD4644F5 /* LayoutConstraintItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayoutConstraintItem.swift; path = Sources/LayoutConstraintItem.swift; sourceTree = ""; };
0F5D34047BAA894BEF1DFA23E6A9DAB1 /* DTCoreTextMacros.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextMacros.h; path = Core/Source/DTCoreTextMacros.h; sourceTree = ""; };
- 0FB543EA6526A50CEA213FD13D39A196 /* RDEPUBWebView+JavaScriptBridge.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBWebView+JavaScriptBridge.swift"; sourceTree = ""; };
- 0FBA6E5012B155499FA6DFF100BC7D79 /* RDEPUBDTCoreTextRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBDTCoreTextRenderer.swift; sourceTree = ""; };
0FC368BD2A8D266EB771E5FB9A5A5B3C /* NSDictionary+DTError.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+DTError.h"; path = "Core/Source/NSDictionary+DTError.h"; sourceTree = ""; };
+ 106026B328A7CCDF60509CACB2AB69BF /* RDReaderView.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; path = RDReaderView.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
+ 10709A2725A69E31B57BB2FD9794062C /* RDEPUBReaderTopToolView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderTopToolView.swift; sourceTree = ""; };
11B604FC3B0D5C91FEA763DCC0BE4DC5 /* DTHTMLParserNode.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLParserNode.m; path = Core/Source/DTHTMLParserNode.m; sourceTree = ""; };
+ 122439DD30580AA0349A6DD05D62A046 /* RDEPUBTextRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextRenderer.swift; sourceTree = ""; };
12A5EA26EE7539EDDA90649D22FB675D /* DTImage+HTML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "DTImage+HTML.m"; path = "Core/Source/DTImage+HTML.m"; sourceTree = ""; };
+ 12DF060DFD7CF87B6793F3F4F8B01F53 /* RDReaderPageChildViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RDReaderPageChildViewController.swift; path = Sources/RDReaderView/RDReaderPageChildViewController.swift; sourceTree = ""; };
13AB6A32A293E3DC21A7047C6DB958CA /* DTHTMLParserTextNode.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLParserTextNode.m; path = Core/Source/DTHTMLParserTextNode.m; sourceTree = ""; };
+ 13B5042913A2E067B77876856E2152BF /* RDEPUBReaderChapterListController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderChapterListController.swift; sourceTree = ""; };
13E1548FC42118F1A4164739DE6BE4E9 /* UIFont+DTCoreText.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIFont+DTCoreText.m"; path = "Core/Source/UIFont+DTCoreText.m"; sourceTree = ""; };
+ 1588FD9ABD6F9A35DD9047F1B0FC962B /* RDEPUBReadingSession.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReadingSession.swift; sourceTree = ""; };
15941683D394FB8D7EAE91CD2CF03FF1 /* UIImage+DTFoundation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+DTFoundation.h"; path = "Core/Source/iOS/UIImage+DTFoundation.h"; sourceTree = ""; };
- 1742D747372CA5272389D6847267DA60 /* RDEPUBReadingModels.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReadingModels.swift; sourceTree = ""; };
174FC0CA05D20BB995E211DE5A0857DB /* Archive+MemoryFile.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Archive+MemoryFile.swift"; path = "Sources/ZIPFoundation/Archive+MemoryFile.swift"; sourceTree = ""; };
1756C6FFCA695290BA62DA4E7B6DD20B /* NSFileWrapper+DTCopying.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSFileWrapper+DTCopying.m"; path = "Core/Source/NSFileWrapper+DTCopying.m"; sourceTree = ""; };
17F750F38183302BF345F40F5A4CDD43 /* NSData+DTCrypto.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSData+DTCrypto.m"; path = "Core/Source/NSData+DTCrypto.m"; sourceTree = ""; };
18387159515E0523AFB4204B1AF940ED /* SSAlertSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SSAlertSwift.debug.xcconfig; sourceTree = ""; };
- 18ED210243FE63DA18C3696132AC3BEC /* RDEPUBModels.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBModels.swift; sourceTree = ""; };
- 197AC6C8795B18D61852C4A3E063F8BC /* RDEPUBNavigatorLayoutContext.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBNavigatorLayoutContext.swift; sourceTree = ""; };
+ 19110636AAFDFF4EB74845D9AFAE5A4E /* RDReaderView-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "RDReaderView-dummy.m"; sourceTree = ""; };
1ABC2F552388CD08EC1EFDEF606AE222 /* DTTextAttachmentHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTTextAttachmentHTMLElement.m; path = Core/Source/DTTextAttachmentHTMLElement.m; sourceTree = ""; };
1ACA5E519F100582F0D772FCD76125C0 /* NSString+DTFormatNumbers.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+DTFormatNumbers.m"; path = "Core/Source/NSString+DTFormatNumbers.m"; sourceTree = ""; };
1AD502A1FA6EB1E38200B5283F979504 /* Pods-ReadViewDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ReadViewDemo.release.xcconfig"; sourceTree = ""; };
- 1B0DCA31DB8359A87C75F5CB5E58893A /* RDEPUBWebContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBWebContentView.swift; sourceTree = ""; };
1B443D035BA5652B03A41BE905C5AC17 /* DTPieProgressIndicator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTPieProgressIndicator.h; path = Core/Source/iOS/DTPieProgressIndicator.h; sourceTree = ""; };
+ 1C5DB66CA8931324F68042711F94DFC9 /* RDReaderToolView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderToolView.swift; sourceTree = ""; };
1C8F0DCEAA5A3DF38C503CA30DC94C1C /* Archive.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Archive.swift; path = Sources/ZIPFoundation/Archive.swift; sourceTree = ""; };
1D4C9B7ED5E04C216AB28E3A4065C1C5 /* DTHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLElement.m; path = Core/Source/DTHTMLElement.m; sourceTree = ""; };
1D6DA580F40DF113709E15CF6542A35C /* DTCoreTextLayouter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextLayouter.m; path = Core/Source/DTCoreTextLayouter.m; sourceTree = ""; };
+ 1DD417BD314029A21D99E7DB185D7E97 /* RDEPUBFixedLayoutTemplate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBFixedLayoutTemplate.swift; sourceTree = ""; };
1E89FE5F2996336E862E82C1E14E0EE7 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = Sources/ZIPFoundation/Resources/PrivacyInfo.xcprivacy; sourceTree = ""; };
1F090A0289338B14050FAF16C22605B0 /* ConstraintConstantTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintConstantTarget.swift; path = Sources/ConstraintConstantTarget.swift; sourceTree = ""; };
2047430DF5C84E0A8FC8874A2B915FC3 /* Pods-ReadViewDemo-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-ReadViewDemo-acknowledgements.markdown"; sourceTree = ""; };
@@ -541,67 +544,76 @@
21BAA93E67D8CBC5297302D798FB4F7A /* DTAttributedLabel.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAttributedLabel.m; path = Core/Source/DTAttributedLabel.m; sourceTree = ""; };
2280F2302F54E90122747821FE681543 /* SSAlertViewExtention.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SSAlertViewExtention.swift; path = Sources/SSAlertSwift/SSAlertViewExtention.swift; sourceTree = ""; };
238893C9CBD95CB9BDF069EB5A303E17 /* RDReaderView */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = RDReaderView; path = RDReaderView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- 23BF5DD0E252AFC65995DDB96AE61B9B /* RDEPUBPublication.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPublication.swift; sourceTree = ""; };
+ 24330A8818509BED791AACF25F76FAC5 /* RDEPUBReaderBottomToolView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderBottomToolView.swift; sourceTree = ""; };
249747495DE8349E480E4789B12D366D /* SnapKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SnapKit.modulemap; sourceTree = ""; };
+ 253099942A3CAA07578C7C4CEF760BAF /* RDReaderEditView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderEditView.swift; sourceTree = ""; };
+ 25752F6845CAD02E33A5C41D9591BD7C /* RDEPUBTextLayoutFrame.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextLayoutFrame.swift; sourceTree = ""; };
25F52D9C765ED2FA87834D4310203E0B /* DTFoundation-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "DTFoundation-prefix.pch"; sourceTree = ""; };
2702E05AA7A266B7D8D0F881FD4E09DA /* DTCompatibility.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCompatibility.h; path = Core/Source/DTCompatibility.h; sourceTree = ""; };
27205D159B39090908CA0E9C3997B784 /* NSDictionary+DTCoreText.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+DTCoreText.h"; path = "Core/Source/NSDictionary+DTCoreText.h"; sourceTree = ""; };
27B7D3C6F0C845E3D42BBA613FFB0465 /* FileManager+ZIP.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "FileManager+ZIP.swift"; path = "Sources/ZIPFoundation/FileManager+ZIP.swift"; sourceTree = ""; };
28043173CA3B3787DC401ACEB5398108 /* DTCoreTextLayoutFrame+Cursor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DTCoreTextLayoutFrame+Cursor.h"; path = "Core/Source/DTCoreTextLayoutFrame+Cursor.h"; sourceTree = ""; };
28D62D3200AC3D4E42C81CDFF7E884E8 /* ConstraintItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintItem.swift; path = Sources/ConstraintItem.swift; sourceTree = ""; };
- 2B867F4889E706AD274C03EBB4642382 /* RDEPUBReaderToolView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderToolView.swift; sourceTree = ""; };
2BA19BD582C1BD54AF673014C04848B6 /* DTFoundation-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "DTFoundation-Info.plist"; sourceTree = ""; };
+ 2BCEA44CE11268C0D82A87D73B9FC906 /* RDReaderView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RDReaderView.swift; path = Sources/RDReaderView/RDReaderView.swift; sourceTree = ""; };
2C50CAB4241C141EF6EC9ED22AB3A7E8 /* NSDictionary+DTCoreText.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+DTCoreText.m"; path = "Core/Source/NSDictionary+DTCoreText.m"; sourceTree = ""; };
2CD63BBDA1CB02B15A54D2CDD8A3ACD7 /* SnapKit-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SnapKit-prefix.pch"; sourceTree = ""; };
2D36F7CDCD7D5788CDCB3A1421DB242B /* NSURL+DTAppLinks.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSURL+DTAppLinks.m"; path = "Core/Source/iOS/NSURL+DTAppLinks.m"; sourceTree = ""; };
2D3D7C8444F58D9B3FCB4F3006D2CFBA /* NSAttributedString+HTML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSAttributedString+HTML.h"; path = "Core/Source/NSAttributedString+HTML.h"; sourceTree = ""; };
2DE4FA5133891593CB45AF36B8EBF60C /* SSAlertSwift-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SSAlertSwift-dummy.m"; sourceTree = ""; };
- 2F69FCFB88B9F1663A813C2BF85531B8 /* RDPlainTextBookBuilder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDPlainTextBookBuilder.swift; sourceTree = ""; };
+ 2F23E4422C91F1DFB7F0C23F0FC9A091 /* RDURLReaderController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RDURLReaderController.swift; path = Sources/RDReaderView/RDURLReaderController.swift; sourceTree = ""; };
306477706BBB1E784F55BCF8EB1AD737 /* Pods-ReadViewDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ReadViewDemo.debug.xcconfig"; sourceTree = ""; };
+ 3125023D51548B527DE43DDA360B494F /* RDEPUBSearchModels.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBSearchModels.swift; sourceTree = ""; };
31BD7533867336DEA9B835F471767178 /* NSString+DTURLEncoding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+DTURLEncoding.h"; path = "Core/Source/NSString+DTURLEncoding.h"; sourceTree = ""; };
31F3F91D5624C451E5D44D85B1DDE7C5 /* NSMutableString+HTML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSMutableString+HTML.m"; path = "Core/Source/NSMutableString+HTML.m"; sourceTree = ""; };
32A5ABB8F3656A110B955B0306E9A8C3 /* ConstraintInsetTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintInsetTarget.swift; path = Sources/ConstraintInsetTarget.swift; sourceTree = ""; };
32A8BED62113FF47E3925378065FA3BE /* SSAlertSwift-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SSAlertSwift-prefix.pch"; sourceTree = ""; };
32FB2B1B0498DCBE7B79BDF49E963E3D /* DTHTMLParser.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLParser.m; path = Core/Source/DTHTMLParser/DTHTMLParser.m; sourceTree = ""; };
+ 33BF79A8AA962086A6F22DE18806F46A /* ResourceBundle-RDReaderViewAssets-RDReaderView-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-RDReaderViewAssets-RDReaderView-Info.plist"; sourceTree = ""; };
+ 33D41502ACD101EC5DFBB42664EF720F /* RDEPUBTextBookBuilder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextBookBuilder.swift; sourceTree = ""; };
33DB57DF8C5AE334B1BBE9F989352F1D /* DTCustomColoredAccessory.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCustomColoredAccessory.h; path = Core/Source/iOS/DTCustomColoredAccessory.h; sourceTree = ""; };
- 349D1AF936EC0325D3F8D20BC3F7CBFA /* RDEPUBPreferences.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPreferences.swift; sourceTree = ""; };
+ 33E993B886F540EC4775478575199F34 /* RDEPUBRenderRequest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBRenderRequest.swift; sourceTree = ""; };
+ 3407CEC2D9B906B36A90E8C7D8DBEE77 /* RDReaderCoverView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderCoverView.swift; sourceTree = ""; };
354C5DD0ACC742762200A640ADA859C6 /* NSCoder+DTCompatibility.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSCoder+DTCompatibility.h"; path = "Core/Source/NSCoder+DTCompatibility.h"; sourceTree = ""; };
+ 35A4374BE871FC7B88D2F8367054063F /* RDEPUBReaderHighlightsViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderHighlightsViewController.swift; sourceTree = ""; };
365FB4C3E0FF364C076EE4E9E47C32EA /* DTCoreText.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = DTCoreText.modulemap; sourceTree = ""; };
36FCB869246FE45FD1C9609A34EB4E74 /* DTCoreTextGlyphRun.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextGlyphRun.m; path = Core/Source/DTCoreTextGlyphRun.m; sourceTree = ""; };
375179DD3A4576CEF947E1760BBDC69B /* DTBlockFunctions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTBlockFunctions.m; path = Core/Source/DTBlockFunctions.m; sourceTree = ""; };
+ 38848FEE38CF2D58126C89D2B90F17C3 /* RDReaderTopToolView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderTopToolView.swift; sourceTree = ""; };
392A75EAF280876545D14AA00517BA4C /* ConstraintMakerEditable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMakerEditable.swift; path = Sources/ConstraintMakerEditable.swift; sourceTree = ""; };
3B610E558C4917C4257DC198EAFEE2F6 /* DTCSSStylesheet.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCSSStylesheet.h; path = Core/Source/DTCSSStylesheet.h; sourceTree = ""; };
3B931AB189BDDE2CCE8578B50BD60AF0 /* DTIframeTextAttachment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTIframeTextAttachment.h; path = Core/Source/DTIframeTextAttachment.h; sourceTree = ""; };
- 3BCA9AFA3BFA7B321228218BEB960BC8 /* RDEPUBTextRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextRenderer.swift; sourceTree = ""; };
+ 3CA82B3E5B177B2456D28B7EB7D94D1F /* RDEPUBReaderDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderDelegate.swift; sourceTree = ""; };
+ 3D5F97E4E9A92968E446569772486F37 /* RDReaderManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderManager.swift; sourceTree = ""; };
3DC78D0B0F37BF9AD1058CACA9AAF9BE /* Data+Serialization.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Data+Serialization.swift"; path = "Sources/ZIPFoundation/Data+Serialization.swift"; sourceTree = ""; };
- 415C064D271E9E8356C242E3043DB6F2 /* RDEPUBReaderDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderDelegate.swift; sourceTree = ""; };
- 41ABB15C697798810A88528D70798D94 /* RDEPUBReaderPersistence.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderPersistence.swift; sourceTree = ""; };
- 41E1A4E33EBA7B1301F94F8125D09C19 /* RDReaderEPUBContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderEPUBContentView.swift; sourceTree = ""; };
4219E7C32019899890CAB66E5FBDF524 /* UIView+DTFoundation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+DTFoundation.m"; path = "Core/Source/iOS/UIView+DTFoundation.m"; sourceTree = ""; };
424F12BEB0EDAAF1C719B5103D30C653 /* DTAnchorHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTAnchorHTMLElement.h; path = Core/Source/DTAnchorHTMLElement.h; sourceTree = ""; };
426D442C01742E4361F5BC34D43152A6 /* DTImageTextAttachment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTImageTextAttachment.h; path = Core/Source/DTImageTextAttachment.h; sourceTree = ""; };
+ 4339C27231D120EC2EC86F021CF5DC16 /* RDReaderEndView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderEndView.swift; sourceTree = ""; };
43CFADE0F46014990052AD67F17C86FF /* DTAccessibilityViewProxy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAccessibilityViewProxy.m; path = Core/Source/DTAccessibilityViewProxy.m; sourceTree = ""; };
4428633C8EC3F6289CCF376F588CE0C4 /* NSString+HTML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+HTML.m"; path = "Core/Source/NSString+HTML.m"; sourceTree = ""; };
+ 445AD03473D9958284B5E463FD97C435 /* SSChapterListController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SSChapterListController.swift; sourceTree = ""; };
4472755F83C121DE5E1E505D145F6DBE /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/CoreText.framework; sourceTree = DEVELOPER_DIR; };
+ 452EABEB04AADDB7964F46326FBDA5BE /* RDReaderView-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RDReaderView-umbrella.h"; sourceTree = ""; };
45C7129757B54DFD9E445D0A17D73A83 /* ConstraintPriority.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintPriority.swift; path = Sources/ConstraintPriority.swift; sourceTree = ""; };
4600E9C0EF3B23864C9D7E5E90155F9D /* SSAlertSwift.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SSAlertSwift.modulemap; sourceTree = ""; };
4684B406B325B35F39322D75B59B6479 /* ConstraintLayoutGuideDSL.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintLayoutGuideDSL.swift; path = Sources/ConstraintLayoutGuideDSL.swift; sourceTree = ""; };
477A0FC2A1F0B7F7CCF72D201ECEBAB0 /* DTDictationPlaceholderTextAttachment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTDictationPlaceholderTextAttachment.m; path = Core/Source/DTDictationPlaceholderTextAttachment.m; sourceTree = ""; };
+ 488C9F0D7B36673ABF0CAFECBB38D91D /* RDEPUBNavigatorLayoutContext.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBNavigatorLayoutContext.swift; sourceTree = ""; };
48A4A6EDC08C8AC933D0DCD507E9D173 /* ZIPFoundation */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = ZIPFoundation; path = ZIPFoundation.framework; sourceTree = BUILT_PRODUCTS_DIR; };
49A21AE1A0AB5383F895B34EA7D6973D /* DTCoreTextGlyphRun.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextGlyphRun.h; path = Core/Source/DTCoreTextGlyphRun.h; sourceTree = ""; };
- 49B01390913A39CC12CB719F529BC1EE /* RDReaderController+EPUBText.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDReaderController+EPUBText.swift"; sourceTree = ""; };
4A06A36ADBE678DB44158D32E751FAFF /* ZIPFoundation-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "ZIPFoundation-dummy.m"; sourceTree = ""; };
4A2659DB0685E16ABAE0382F726DA8C8 /* Archive+Helpers.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Archive+Helpers.swift"; path = "Sources/ZIPFoundation/Archive+Helpers.swift"; sourceTree = ""; };
4AF6156128A1A0CA24D9D06E346B9445 /* DTVersion.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTVersion.m; path = Core/Source/DTVersion.m; sourceTree = ""; };
4BEC60CB3EDA1C189D3B062E29217440 /* ZIPFoundation.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = ZIPFoundation.debug.xcconfig; sourceTree = ""; };
4C2B218B5EAA5831A036BBF7E1EFC1B9 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/MediaPlayer.framework; sourceTree = DEVELOPER_DIR; };
+ 4C48433D1C85ADDC616F469F5369EBF1 /* RDEPUBWebView+JavaScriptBridge.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBWebView+JavaScriptBridge.swift"; sourceTree = ""; };
4D641809A99849F80FDCB02F8CE8D6A0 /* Data+CompressionDeprecated.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Data+CompressionDeprecated.swift"; path = "Sources/ZIPFoundation/Data+CompressionDeprecated.swift"; sourceTree = ""; };
- 4DAD5578B00491EBE9F71E4C310BEAB2 /* RDEPUBParser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBParser.swift; sourceTree = ""; };
4EEFAE80193E1F0DA53400C9AD641410 /* DTActivityTitleView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTActivityTitleView.m; path = Core/Source/iOS/DTActivityTitleView.m; sourceTree = ""; };
4F0486D3B46DC92AB254D6EE9CC233BF /* DTAccessibilityElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAccessibilityElement.m; path = Core/Source/DTAccessibilityElement.m; sourceTree = ""; };
50297AAB8278F2BB6FCFE82B20C7A049 /* ConstraintLayoutSupport.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintLayoutSupport.swift; path = Sources/ConstraintLayoutSupport.swift; sourceTree = ""; };
- 50F9C7B828F676C6EA37BDFC29FE9047 /* RDEPUBParser+TOC.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBParser+TOC.swift"; sourceTree = ""; };
5155CCDD61E030184249A020FE5D74B0 /* DTWeakSupport.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTWeakSupport.h; path = Core/Source/DTWeakSupport.h; sourceTree = ""; };
+ 516C8F88FE57FE1637284CEADF7FCF26 /* RDEPUBDTCoreTextRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBDTCoreTextRenderer.swift; sourceTree = ""; };
5245010774F8F1C4857763CC7B96E7B4 /* ConstraintMakerExtendable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMakerExtendable.swift; path = Sources/ConstraintMakerExtendable.swift; sourceTree = ""; };
533CFFF04BE7BC4BDEAE11DF675A84CA /* DTBreakHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTBreakHTMLElement.h; path = Core/Source/DTBreakHTMLElement.h; sourceTree = ""; };
53499F40E1790A6CBE8AFEB3AC5BEFC8 /* NSMutableArray+DTMoving.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSMutableArray+DTMoving.m"; path = "Core/Source/NSMutableArray+DTMoving.m"; sourceTree = ""; };
@@ -611,47 +623,48 @@
53D473C6F07982D3D25781A343EC5A99 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = Sources/PrivacyInfo.xcprivacy; sourceTree = ""; };
53F98F0F060B066CAFBCD3C85C26B2B4 /* DTCoreText.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreText.h; path = Core/Source/DTCoreText.h; sourceTree = ""; };
557DA029E15EC99631B647AE8922007F /* NSURL+DTAppLinks.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSURL+DTAppLinks.h"; path = "Core/Source/iOS/NSURL+DTAppLinks.h"; sourceTree = ""; };
+ 559CDABFD5C6AC0A1D479DE506EBC216 /* RDEPUBPreferences.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPreferences.swift; sourceTree = ""; };
56489D9BA834497E48438A4B4E1CCCFF /* DTCoreTextFontDescriptor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextFontDescriptor.h; path = Core/Source/DTCoreTextFontDescriptor.h; sourceTree = ""; };
5679D281C282896E6FC352C48E0AF439 /* DTAttributedLabel.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTAttributedLabel.h; path = Core/Source/DTAttributedLabel.h; sourceTree = ""; };
- 58281C9CDEC881CBF5E6D28D119E5D23 /* RDReaderEndView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderEndView.swift; sourceTree = ""; };
+ 58A9D8548F1959F0F5B6804CF07F613A /* RDEPUBParser+Package.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBParser+Package.swift"; sourceTree = ""; };
+ 595F8E6BBA2C2F59D77807DBAC5569A5 /* RDEPUBStyleSheetBuilder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBStyleSheetBuilder.swift; sourceTree = ""; };
5964AF1336E2F0DA0E2C32991E76C78C /* Data+Compression.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Data+Compression.swift"; path = "Sources/ZIPFoundation/Data+Compression.swift"; sourceTree = ""; };
5977986EDFE76D5EF96A6D95483B12F7 /* Pods-ReadViewDemo-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-ReadViewDemo-frameworks.sh"; sourceTree = ""; };
+ 5A107E97C03EEEAF221E2E6A7DE9D530 /* RDEPUBParser+TOC.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBParser+TOC.swift"; sourceTree = ""; };
5A9A82CB094636AFB53D6D130D6219BA /* ConstraintLayoutGuide+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ConstraintLayoutGuide+Extensions.swift"; path = "Sources/ConstraintLayoutGuide+Extensions.swift"; sourceTree = ""; };
5B007AEEACE9EA271DD73AFD80A74D83 /* DTLog.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTLog.m; path = Core/Source/DTLog.m; sourceTree = ""; };
5BA251BAC8B430B3A4D4C4321046B990 /* Archive+Deprecated.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Archive+Deprecated.swift"; path = "Sources/ZIPFoundation/Archive+Deprecated.swift"; sourceTree = ""; };
- 5C7E829F5BEE0B03156583C8500D6913 /* RDReaderCommon.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderCommon.swift; sourceTree = ""; };
+ 5C301D24F51B4532911A60156CB37626 /* RDEPUBPublication.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPublication.swift; sourceTree = ""; };
+ 5D2E0632761004082322B9F4EDC63E17 /* RDEPUBSearchEngine.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBSearchEngine.swift; sourceTree = ""; };
5D62C5531667F364F14DB67D33FFA554 /* DTCoreTextParagraphStyle.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextParagraphStyle.h; path = Core/Source/DTCoreTextParagraphStyle.h; sourceTree = ""; };
- 5D9A9E0B80E0DED0838BD11BF64B6A4B /* RDEPUBParser+Archive.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBParser+Archive.swift"; sourceTree = ""; };
5DA1E8494B86FFD79C639DCB8C21E1BB /* DTPieProgressIndicator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTPieProgressIndicator.m; path = Core/Source/iOS/DTPieProgressIndicator.m; sourceTree = ""; };
5E791886E59DC0FAB02C6BBAB5BE5B6B /* DTCoreTextFontCollection.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextFontCollection.m; path = Core/Source/DTCoreTextFontCollection.m; sourceTree = ""; };
+ 5F3B149D311344D829E4A72CDAE18122 /* RDEPUBReaderToolView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderToolView.swift; sourceTree = ""; };
5F760D62F6C6F083374702146337F2CB /* DTCoreTextParagraphStyle.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextParagraphStyle.m; path = Core/Source/DTCoreTextParagraphStyle.m; sourceTree = ""; };
5F991240340FA026C71B68EB25B9F9C8 /* Entry+Serialization.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Entry+Serialization.swift"; path = "Sources/ZIPFoundation/Entry+Serialization.swift"; sourceTree = ""; };
5FAB3C7F19A511C2B1B7DFBA3CA308C4 /* DTFoundation-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "DTFoundation-umbrella.h"; sourceTree = ""; };
6014DA7213E4687224F2BF921E30002C /* DTFoundation.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = DTFoundation.debug.xcconfig; sourceTree = ""; };
- 6061E339364E22A1C8EC52EB3E7BAB0C /* RDURLReaderController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RDURLReaderController.swift; path = Sources/RDReaderView/RDURLReaderController.swift; sourceTree = ""; };
60657D08CCDB38BFED30BD40F4720CE4 /* ConstraintMaker.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMaker.swift; path = Sources/ConstraintMaker.swift; sourceTree = ""; };
- 60EFBE79C0CB86069AE38B09BCF5E216 /* RDReaderView-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RDReaderView-umbrella.h"; sourceTree = ""; };
61E36C4FE448DDDA03450847FCB785B6 /* Archive+BackingConfiguration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Archive+BackingConfiguration.swift"; path = "Sources/ZIPFoundation/Archive+BackingConfiguration.swift"; sourceTree = ""; };
- 621689764606AC8062EBDE6D594D7829 /* RDEPUBReadingSession.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReadingSession.swift; sourceTree = ""; };
+ 6240FA08DE0A138D4C960569008F86D3 /* RDReaderView-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RDReaderView-prefix.pch"; sourceTree = ""; };
63A6EDF10DA5A051C6CDCBE5D97AF11E /* DTVideoTextAttachment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTVideoTextAttachment.m; path = Core/Source/DTVideoTextAttachment.m; sourceTree = ""; };
- 6429C35065EBCF2BF337B6E4B690F9F0 /* RDReaderView-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "RDReaderView-Info.plist"; sourceTree = ""; };
+ 6473B31CD4B4F64ED6B4C4A6707C15D1 /* RDReaderView-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "RDReaderView-Info.plist"; sourceTree = ""; };
647FB0C5AAF84C71A7784DD277E48910 /* UIFont+DTCoreText.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIFont+DTCoreText.h"; path = "Core/Source/UIFont+DTCoreText.h"; sourceTree = ""; };
64868ADD0419A5D112863F638932DA34 /* ZIPFoundation-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ZIPFoundation-Info.plist"; sourceTree = ""; };
64AB89EAB10148C39B726B26BE04F4F8 /* DTCoreTextLayoutLine.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextLayoutLine.h; path = Core/Source/DTCoreTextLayoutLine.h; sourceTree = ""; };
- 64B825BF76778E9B12A926734F1AF152 /* RDReaderPageChildViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RDReaderPageChildViewController.swift; path = Sources/RDReaderView/RDReaderPageChildViewController.swift; sourceTree = ""; };
6578DFAB51436E8DF266FD087700E26B /* ConstraintDescription.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintDescription.swift; path = Sources/ConstraintDescription.swift; sourceTree = ""; };
+ 676181142A1274D18FC2C2905C411080 /* SSEventTrigger.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SSEventTrigger.swift; sourceTree = ""; };
+ 67A77A22862EBE7AC618AD500E7DA7E1 /* RDEPUBParser+Resources.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBParser+Resources.swift"; sourceTree = ""; };
+ 68492190D405DF22F3253362857EFBA3 /* RDReaderEPUBContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderEPUBContentView.swift; sourceTree = ""; };
6A1EBB9497F4846C84CA0BF5E4F84FC7 /* DTLog.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTLog.h; path = Core/Source/DTLog.h; sourceTree = ""; };
- 6A460891E141702B09E55886799672F5 /* RDEPUBWebView+Reflowable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBWebView+Reflowable.swift"; sourceTree = ""; };
- 6B5F83842C5F5BAD9DC241FAB17E86C7 /* RDEPUBTextSearchEngine.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextSearchEngine.swift; sourceTree = ""; };
6B96B0998B49D8E149C995474DA5FE5F /* DTHTMLWriter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTHTMLWriter.h; path = Core/Source/DTHTMLWriter.h; sourceTree = ""; };
- 6BDF97DAA7A9F8075754F7800D54E4CE /* RDEPUBSearchModels.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBSearchModels.swift; sourceTree = ""; };
6C647A2EB2720BCBF88B160D124B57BF /* UILayoutSupport+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UILayoutSupport+Extensions.swift"; path = "Sources/UILayoutSupport+Extensions.swift"; sourceTree = ""; };
+ 6D165714534D2B0ADBCF337C9F45F58C /* RDEPUBNavigatorState.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBNavigatorState.swift; sourceTree = ""; };
6D2EC0B845131A88247902AEA26F40AE /* SSAlertAnimation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SSAlertAnimation.swift; path = Sources/SSAlertSwift/SSAlertAnimation.swift; sourceTree = ""; };
6D86C7063D1128EAEB3A30EBEF214B3F /* NSString+DTFormatNumbers.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+DTFormatNumbers.h"; path = "Core/Source/NSString+DTFormatNumbers.h"; sourceTree = ""; };
- 6E30451C7CFBBBA73D81F8A206611499 /* RDEPUBFixedLayoutTemplate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBFixedLayoutTemplate.swift; sourceTree = ""; };
- 704B13E62616203BD58554B42C37B039 /* RDEPUBReaderSettings.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderSettings.swift; sourceTree = ""; };
+ 6DE57FE714CC9010A030F3AB1AF8B8CC /* RDReaderTheme.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderTheme.swift; sourceTree = ""; };
+ 6F4C301C54B47F4B950AD3D60FBC3209 /* RDEPUBWebView+Reflowable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBWebView+Reflowable.swift"; sourceTree = ""; };
70F5D03B9B3B7F182C8F5C1F5D4FA7E7 /* DTTiledLayerWithoutFade.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTTiledLayerWithoutFade.m; path = Core/Source/iOS/DTTiledLayerWithoutFade.m; sourceTree = ""; };
- 7126988BFC0706CC32ADD178FD4BD05A /* RDReaderTheme.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderTheme.swift; sourceTree = ""; };
713C9FD0354D16AFCCFA630A97F2DA68 /* NSCharacterSet+HTML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSCharacterSet+HTML.m"; path = "Core/Source/NSCharacterSet+HTML.m"; sourceTree = ""; };
7176B677A350927FC670368313DC2CA1 /* ConstraintConfig.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintConfig.swift; path = Sources/ConstraintConfig.swift; sourceTree = ""; };
71E2D85A099C99CD82FD1842EE7C52D2 /* NSAttributedString+DTDebug.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSAttributedString+DTDebug.h"; path = "Core/Source/NSAttributedString+DTDebug.h"; sourceTree = ""; };
@@ -659,35 +672,35 @@
7345CE45F696634CEA692A86D16F684C /* Entry+ZIP64.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Entry+ZIP64.swift"; path = "Sources/ZIPFoundation/Entry+ZIP64.swift"; sourceTree = ""; };
73940D215D644B6815A09DA802CFBDDF /* NSArray+DTError.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSArray+DTError.m"; path = "Core/Source/NSArray+DTError.m"; sourceTree = ""; };
73A99927E52B24583A6A2CF7E6F23848 /* DTVideoTextAttachment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTVideoTextAttachment.h; path = Core/Source/DTVideoTextAttachment.h; sourceTree = ""; };
- 75928584ECA5C8361DC967CA3B3BB3FD /* RDReaderView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RDReaderView.swift; path = Sources/RDReaderView/RDReaderView.swift; sourceTree = ""; };
+ 74F3D8D4765924AA777C5153C89603E9 /* RDReaderGestureController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RDReaderGestureController.swift; path = Sources/RDReaderView/RDReaderGestureController.swift; sourceTree = ""; };
75C5986EF2D63801A8A30B9D77A7FDD0 /* ResourceBundle-ZIPFoundation_Privacy-ZIPFoundation-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-ZIPFoundation_Privacy-ZIPFoundation-Info.plist"; sourceTree = ""; };
+ 75F2DC4A24F7A1558DD65BD6F43C270C /* RDEPUBParser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBParser.swift; sourceTree = ""; };
75FA8E843D9225BD65EB9D17825EC0F4 /* NSCharacterSet+HTML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSCharacterSet+HTML.h"; path = "Core/Source/NSCharacterSet+HTML.h"; sourceTree = ""; };
+ 76B26D09BDAD5ED7601C6B3F8E6012FC /* RDEPUBTextRendererSupport.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextRendererSupport.swift; sourceTree = ""; };
76F99C8130EE83A22CD223C7F5E6E66E /* NSAttributedString+SmallCaps.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSAttributedString+SmallCaps.m"; path = "Core/Source/NSAttributedString+SmallCaps.m"; sourceTree = ""; };
77213203802F44012346181FED2856F9 /* UIApplication+DTNetworkActivity.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIApplication+DTNetworkActivity.m"; path = "Core/Source/iOS/UIApplication+DTNetworkActivity.m"; sourceTree = ""; };
77A613841EB90827AF321DD50B21DB5D /* DTAttributedTextView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAttributedTextView.m; path = Core/Source/DTAttributedTextView.m; sourceTree = ""; };
- 77D17BD78B36A019C8056CFC25E59881 /* RDEPUBTextPaging.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextPaging.swift; sourceTree = ""; };
78873FAFC0AD9E930D6050A7F976714B /* DTCoreText.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = DTCoreText.debug.xcconfig; sourceTree = ""; };
78EA0DF405F8465A86BFD3DDFD2EC295 /* Pods-ReadViewDemo */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-ReadViewDemo"; path = Pods_ReadViewDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- 78FBB07DBF1E01A21CC137A18701BD55 /* SSChapterListController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SSChapterListController.swift; sourceTree = ""; };
79A77584AB3B92B2508AE0BCFCB8FD87 /* UIView+DTFoundation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+DTFoundation.h"; path = "Core/Source/iOS/UIView+DTFoundation.h"; sourceTree = ""; };
7AB9016408894675D11069CDAACEDE94 /* DTImage+HTML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DTImage+HTML.h"; path = "Core/Source/DTImage+HTML.h"; sourceTree = ""; };
- 7AC8A02C88E03D6E08BAC3B5313BC4E9 /* RDReaderFlowLayout.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RDReaderFlowLayout.swift; path = Sources/RDReaderView/RDReaderFlowLayout.swift; sourceTree = ""; };
+ 7B0DCFED92BB1D432F8FCC6879458111 /* RDReaderView.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = RDReaderView.release.xcconfig; sourceTree = ""; };
7C496AB6A73C2D0BD0FF340A6A2960D5 /* DTHTMLAttributedStringBuilder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLAttributedStringBuilder.m; path = Core/Source/DTHTMLAttributedStringBuilder.m; sourceTree = ""; };
7D63AE1801C7AB149219317BE7529B26 /* DTAnchorHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAnchorHTMLElement.m; path = Core/Source/DTAnchorHTMLElement.m; sourceTree = ""; };
- 7E6E393B573F355F89671CA4F939665B /* RDEPUBReaderController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderController.swift; sourceTree = ""; };
+ 7E78734521FC2C49E81DEFAA400638CD /* RDReaderCommon.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderCommon.swift; sourceTree = ""; };
7F051D813D50D20D53205D5FDCAA877C /* UIScreen+DTFoundation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIScreen+DTFoundation.m"; path = "Core/Source/iOS/UIScreen+DTFoundation.m"; sourceTree = ""; };
- 802A3A8F8886D5ED2B798481AC345609 /* RDEPUBParser+ReadingProfile.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBParser+ReadingProfile.swift"; sourceTree = ""; };
+ 80722DEDCE690F56237D5706BCEA3B55 /* RDEPUBWebView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBWebView.swift; sourceTree = ""; };
80B3D15ED0D19D020663EF055386291A /* DTColor+Compatibility.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DTColor+Compatibility.h"; path = "Core/Source/DTColor+Compatibility.h"; sourceTree = ""; };
81B8F42440B802E6EC2B1FF7E3022F3A /* SSAlertDefaultAnmation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SSAlertDefaultAnmation.swift; path = Sources/SSAlertSwift/SSAlertDefaultAnmation.swift; sourceTree = ""; };
8272D0B59ABD6652D0CAE3DFCC2C5EE1 /* DTActivityTitleView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTActivityTitleView.h; path = Core/Source/iOS/DTActivityTitleView.h; sourceTree = ""; };
83410CC9CF2ABE63B90A92F2F988BF65 /* RDReaderView-RDReaderViewAssets */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "RDReaderView-RDReaderViewAssets"; path = RDReaderViewAssets.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
83E37DC74BB53F90A5AD54842858A62F /* NSString+Paragraphs.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+Paragraphs.h"; path = "Core/Source/NSString+Paragraphs.h"; sourceTree = ""; };
- 84D644BDD3105BC08DA9C544E19306C8 /* RDEPUBTextPaginationSupport.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextPaginationSupport.swift; sourceTree = ""; };
8552BE1A1F6A6D2879763DE3B2B8A2DD /* NSNumber+RomanNumerals.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSNumber+RomanNumerals.h"; path = "Core/Source/NSNumber+RomanNumerals.h"; sourceTree = ""; };
+ 85F40FA4C291CC3FC36B3FD11E0A65ED /* RDEPUBWebView+Search.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBWebView+Search.swift"; sourceTree = ""; };
86A0361390AA0C29BC1A5487083A12A7 /* DTColorFunctions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTColorFunctions.h; path = Core/Source/DTColorFunctions.h; sourceTree = ""; };
882FA707ED3513D263A1E0656AAD2D33 /* ConstraintPriorityTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintPriorityTarget.swift; path = Sources/ConstraintPriorityTarget.swift; sourceTree = ""; };
885653999BD237A574B67DB279DE7E87 /* ConstraintRelatableTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintRelatableTarget.swift; path = Sources/ConstraintRelatableTarget.swift; sourceTree = ""; };
- 89537ECF9C470CDEBAABD38E17070806 /* RDEPUBStyleSheetBuilder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBStyleSheetBuilder.swift; sourceTree = ""; };
+ 899BBF53253C6175CDD9F3145E3354D8 /* RDPlainTextBookBuilder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDPlainTextBookBuilder.swift; sourceTree = ""; };
8ABDEFEA94DC6408D92AC66DA3625DE5 /* NSString+HTML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+HTML.h"; path = "Core/Source/NSString+HTML.h"; sourceTree = ""; };
8B35D0622F339A5B8A864FAA0C9EAE47 /* Pods-ReadViewDemo-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-ReadViewDemo-acknowledgements.plist"; sourceTree = ""; };
8B59C9A1B265393E4BA8383B308A5917 /* DTHTMLAttributedStringBuilder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTHTMLAttributedStringBuilder.h; path = Core/Source/DTHTMLAttributedStringBuilder.h; sourceTree = ""; };
@@ -696,153 +709,144 @@
8CFEE298DD52246B46E6FA62B16B38DF /* DTTextHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTTextHTMLElement.m; path = Core/Source/DTTextHTMLElement.m; sourceTree = ""; };
8E3E834CCC952717089550F50A2DE59C /* SSAlertSwift-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "SSAlertSwift-Info.plist"; sourceTree = ""; };
8E5637603691B9B35C2A886B686D3CE7 /* ConstraintViewDSL.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintViewDSL.swift; path = Sources/ConstraintViewDSL.swift; sourceTree = ""; };
- 8E90A625BAEFA03771A79C90309E8763 /* RDReaderManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderManager.swift; sourceTree = ""; };
8F2D44B82815CA3397BE2C9F64819B88 /* DTCoreTextFunctions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextFunctions.m; path = Core/Source/DTCoreTextFunctions.m; sourceTree = ""; };
- 8F7E1893259D2E01A812A5A423628EA7 /* RDEPUBNavigatorState.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBNavigatorState.swift; sourceTree = ""; };
8F950CEF31AC0065C0BEAB26BA2CEFA1 /* ConstraintOffsetTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintOffsetTarget.swift; path = Sources/ConstraintOffsetTarget.swift; sourceTree = ""; };
91144FC099DD458E61625CD939577DAA /* ConstraintDirectionalInsets.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintDirectionalInsets.swift; path = Sources/ConstraintDirectionalInsets.swift; sourceTree = "