diff --git a/Doc/Android EPUB 阅读器实施方案.md b/Doc/Android EPUB 阅读器实施方案.md new file mode 100644 index 0000000..3d7fb65 --- /dev/null +++ b/Doc/Android EPUB 阅读器实施方案.md @@ -0,0 +1,606 @@ +# Android EPUB 阅读器实施方案 + +## Context + +当前 ReadViewSDK 是一个功能完整的 iOS EPUB 阅读器框架(Swift + UIKit),支持 EPUB 2/3 解析、三种渲染模式(重排/固定布局/网页交互)、全文搜索、高亮/书签/批注、分页管理等。目标是基于现有 iOS 版本的架构和 JS 桥接协议,实现一套 Android 版本的 EPUB 阅读器。 + +**核心策略**:JS 桥接层(epub-bridge.js、WeReadApi.js 等)直接复用,Swift 逻辑层用 Kotlin 重写,UIKit UI 层用 Android 原生 View 重写。 + +--- + +## 第一阶段:项目基础设施(预计 1 周) + +### 1.1 创建 Android 项目 + +``` +ReadViewSDK-Android/ +├── app/ # Demo 应用 +├── reader-core/ # Kotlin 核心库(解析、模型、搜索) +├── reader-jsbridge/ # WebView JS 桥接层 +├── reader-ui/ # Android UI 组件(工具栏、搜索面板、设置等) +├── reader-view/ # 翻页容器(ViewPager2 封装) +└── build-logic/ # Gradle convention plugins +``` + +- 语言:Kotlin +- 最低 API:24(Android 7.0) +- 目标 API:34 +- 构建工具:Gradle + Kotlin DSL +- 依赖注入:手动(与 iOS 的 `RDEPUBReaderDependencies` 模式一致) + +### 1.2 Gradle 模块划分 + +| 模块 | 对应 iOS 层 | 职责 | +|------|------------|------| +| `reader-core` | EPUBCore/ + EPUBTextRendering/ | 解析、模型、搜索引擎、CSS 生成、分页算法 | +| `reader-jsbridge` | RDEPUBJavaScriptBridge + JS Resources | JS 文件管理、消息收发、脚本生成 | +| `reader-ui` | EPUBUI/ | 工具栏、搜索面板、设置面板、目录列表、高亮管理 | +| `reader-view` | ReaderView/ | 翻页容器(ViewPager2 + PagerSnapHelper) | + +### 1.3 公共依赖 + +```kotlin +// reader-core +implementation("net.lingala.zip4j:zip4j:2.11.5") // 替代 ZIPFoundation +implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") +implementation("com.google.code.gson:gson:2.11.0") // 替代 Codable +implementation("androidx.webkit:webkit:1.10.0") + +// reader-ui +implementation("androidx.recyclerview:recyclerview:1.3.2") +implementation("com.google.android.material:material:1.12.0") +implementation("androidx.viewpager2:viewpager2:1.0.0") + +// reader-view +implementation("androidx.viewpager2:viewpager2:1.0.0") +implementation("androidx.recyclerview:recyclerview:1.3.2") +``` + +--- + +## 第二阶段:Core 层 — EPUB 解析与模型(预计 2 周) + +### 2.1 数据模型迁移 + +将 iOS 的 Codable struct 迁移为 Kotlin data class: + +| iOS 文件 | Android 文件 | 关键类型 | +|----------|-------------|---------| +| `RDEPUBModels.swift` | `EpubModels.kt` | `EpubLayout`, `EpubMetadata`, `ManifestItem`, `SpineItem`, `TableOfContentsItem` | +| `RDEPUBReadingLocationModels.swift` | `ReadingLocationModels.kt` | `ReadingLocation`, `Viewport`, `ReadingContext` | +| `RDEPUBAnnotationModels.swift` | `AnnotationModels.kt` | `Selection`, `Highlight`, `Bookmark`, `Annotation` | +| `RDEPUBPaginationModels.swift` | `PaginationModels.kt` | `EpubPage`, `ChapterInfo`, `FixedSpread` | +| `RDEPUBSearchModels.swift` | `SearchModels.kt` | `SearchMatch`, `SearchResult`, `SearchState`, `SearchPresentation` | +| `RDEPUBPreferences.swift` | `ReaderPreferences.kt` | `ReadingPreferences`(字体、行高、颜色、分栏) | +| `RDEPUBRenderRequest.swift` | `RenderRequest.kt` | `ReflowableRenderRequest`, `FixedRenderRequest` | +| `RDEPUBReaderConfiguration.swift` | `ReaderConfiguration.kt` | 完整阅读器配置 | +| `RDEPUBReaderTheme.swift` | `ReaderTheme.kt` | 6 种预设主题 | + +### 2.2 EPUB 解析器迁移 + +| iOS 文件 | Android 文件 | 说明 | +|----------|-------------|------| +| `RDEPUBParser.swift` | `EpubParser.kt` | 主解析入口 | +| `RDEPUBParser+Archive.swift` | `EpubParser+Archive.kt` | zip4j 解压(替代 ZIPFoundation) | +| `RDEPUBParser+Package.swift` | `EpubParser+Package.kt` | OPF XML SAX 解析 | +| `RDEPUBParser+TOC.swift` | `EpubParser+TOC.kt` | NCX/Nav Document 解析 | +| `RDEPUBParser+Resources.swift` | `EpubParser+Resources.kt` | HTML/资源文件读取 | +| `RDEPUBParser+ReadingProfile.swift` | `EpubParser+ReadingProfile.kt` | 阅读模式判断 | +| `RDEPUBPublication.swift` | `EpubPublication.kt` | Facade 门面 | +| `RDEPUBResourceResolver.swift` | `ResourceResolver.kt` | URL 规范化 | + +**XML 解析方案**:iOS 使用 `XMLParser`(SAX),Android 使用 `org.xml.sax.XMLReader`(同为 SAX),解析逻辑可逐行对照翻译。 + +### 2.3 搜索引擎迁移 + +| iOS 文件 | Android 文件 | +|----------|-------------| +| `RDEPUBSearchEngine.swift` | `EpubSearchEngine.kt` | +| `RDEPUBTextSearchEngine.swift` | `TextSearchEngine.kt` | + +搜索引擎核心逻辑(遍历 spine → 读取 HTML → 解析为纯文本 → NSString.range 搜索 → 生成 match)可直接翻译为 Kotlin `String.indexOf` 循环。 + +### 2.4 CSS 生成与样式 + +| iOS 文件 | Android 文件 | +|----------|-------------| +| `RDEPUBStyleSheetBuilder.swift` | `StyleSheetBuilder.kt` | +| `RDEPUBFixedLayoutTemplate.swift` | `FixedLayoutTemplate.kt` | + +CSS 生成逻辑完全平台无关,直接翻译即可。 + +### 2.5 分页算法 + +| iOS 文件 | Android 文件 | 说明 | +|----------|-------------|------| +| `RDEPUBPaginator.swift` | `EpubPaginator.kt` | 离屏 WebView 分页计算 | +| `RDEPUBReadingSession.swift` | `ReadingSession.kt` | 状态机 + 页面管理 | + +**关键决策**:Android 版统一使用 WebView 渲染,不实现 `EPUBTextRendering` 层(DTCoreText 的 Android 等价物过于复杂且收益低)。这意味着: + +- **不迁移** `EPUBTextRendering/` 整个目录(18 个文件) +- **不迁移** `EPUBUI/TextPage/` 目录(7 个文件) +- Android 版的所有内容(包括重排模式)都通过 WebView + CSS column 分页实现 +- 这与 iOS 的 `webInteractive` 路径一致,已验证可行 + +--- + +## 第三阶段:JS 桥接层(预计 1 周) + +### 3.1 JS 文件直接复用 + +将以下文件直接复制到 Android `assets/` 目录: + +``` +reader-jsbridge/src/main/assets/js/ +├── epub-bridge.js # 直接复用 +├── WeReadApi.js # 直接复用 +├── cssInjector.js # 直接复用 +├── rangy-core.js # 直接复用 +└── rangy-serializer.js # 直接复用 +``` + +### 3.2 WKWebView → Android WebView 桥接映射 + +| iOS 机制 | Android 等价物 | +|----------|--------------| +| `WKScriptMessageHandler.userContentController(_:didReceive:)` | `@JavascriptInterface` 方法 | +| `window.webkit.messageHandlers.{name}.postMessage()` | 修改 JS 端调用 `AndroidBridge.{method}()` | +| `WKWebView.evaluateJavaScript()` | `WebView.evaluateJavascript()` | +| `WKURLSchemeHandler` | `WebViewClient.shouldInterceptRequest()` | +| `WKUserScript`(页面加载前注入) | `WebView.addJavascriptInterface()` + `WebViewClient.onPageStarted()` | + +### 3.3 Android Bridge 实现 + +```kotlin +// EpubBridge.kt — 对应 iOS RDEPUBJavaScriptBridge +class EpubBridge(private val callback: BridgeCallback) { + + @JavascriptInterface + fun onProgressionChanged(json: String) { callback.onProgressionChanged(json) } + + @JavascriptInterface + fun onSelectionChanged(json: String) { callback.onSelectionChanged(json) } + + @JavascriptInterface + fun onInternalLink(href: String) { callback.onInternalLink(href) } + + @JavascriptInterface + fun onExternalLink(url: String) { callback.onExternalLink(url) } + + @JavascriptInterface + fun onJSError(message: String) { callback.onJSError(message) } + + @JavascriptInterface + fun onFixedLayoutReady() { callback.onFixedLayoutReady() } +} +``` + +**JS 端修改**:需要将 `window.webkit.messageHandlers.xxx.postMessage(data)` 替换为 `window.AndroidBridge.xxx(JSON.stringify(data))`。可以通过在注入时做字符串替换,或维护一个 Android 版本的 bridge JS 文件。 + +### 3.4 WebView 资源拦截 + +对应 iOS 的 `ss-reader://book/` 自定义协议: + +```kotlin +// EpubSchemeHandler.kt +class EpubSchemeHandler(private val publication: EpubPublication) : WebViewClient() { + + override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? { + val url = request.url.toString() + if (url.startsWith("ss-reader://book/")) { + val path = url.removePrefix("ss-reader://book/") + val data = publication.readResource(path) + val mimeType = getMimeType(path) + return WebResourceResponse(mimeType, "UTF-8", data.byteInputStream()) + } + return null + } +} +``` + +### 3.5 JavaScriptBridge 脚本生成 + +对应 iOS 的 `RDEPUBJavaScriptBridge` 中各种 `*Script()` 方法,生成 `evaluateJavascript()` 调用的 JS 代码字符串。这些方法的核心是拼接 JSON 参数 + 调用 `WeReadApi.*` 方法,逻辑完全相同。 + +--- + +## 第四阶段:翻页容器(预计 1.5 周) + +### 4.1 PagerView 实现 + +对应 iOS `RDReaderView`(UICollectionView + UIPageViewController): + +```kotlin +// PagerView.kt — 对应 iOS RDReaderView +class PagerView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null +) : FrameLayout(context, attrs) { + + private val viewPager: ViewPager2 + private val adapter: PageAdapter + + enum class DisplayMode { HORIZONTAL, VERTICAL } + var displayMode = DisplayMode.HORIZONTAL + set(value) { /* 切换 orientation */ } + + fun reloadData() { adapter.notifyDataSetChanged() } + fun scrollToPage(index: Int, animated: Boolean) { /* ... */ } +} +``` + +**PageAdapter**:使用 `RecyclerView.Adapter`,每个 item 是一个 `FrameLayout`,内部放 `WebView`。 + +### 4.2 点击区域检测 + +对应 iOS `RDReaderTapRegionHandler`: + +```kotlin +// TapRegionHandler.kt +class TapRegionHandler { + enum class Region { LEFT, CENTER, RIGHT } + + fun resolve(x: Float, viewWidth: Float): Region { + return when { + x < viewWidth / 3 -> Region.LEFT + x > viewWidth * 2 / 3 -> Region.RIGHT + else -> Region.CENTER + } + } +} +``` + +### 4.3 预加载控制 + +对应 iOS `RDReaderPreloadController`: + +使用 `ViewPager2.offscreenPageLimit` 控制预加载页面数量(默认 1-2 页)。 + +--- + +## 第五阶段:UI 组件(预计 2 周) + +### 5.1 阅读器主控制器 + +对应 iOS `RDEPUBReaderController`: + +```kotlin +// EpubReaderFragment.kt — Fragment 优于 Activity,方便宿主嵌入 +class EpubReaderFragment : Fragment() { + + // 对应 iOS 的各 Coordinator + private lateinit var runtime: ReaderRuntime + private lateinit var pagerView: PagerView + private lateinit var topBar: TopToolBar + private lateinit var bottomBar: BottomToolBar + private lateinit var searchPanel: SearchPanelView + + // Public API + fun openBook(epubUrl: Uri, configuration: ReaderConfiguration = ReaderConfiguration.default) + fun goTo(location: ReadingLocation) + fun goToPageNumber(pageNumber: Int) + fun search(keyword: String) + fun addHighlight(selection: Selection, color: Int, note: String?) + fun addBookmark(note: String?) + // ... 其余 public API +} +``` + +### 5.2 Coordinator 架构迁移 + +保持 iOS 的 Coordinator 模式,每个 Coordinator 作为独立的 Kotlin 类: + +| iOS Coordinator | Android 类 | 职责 | +|----------------|-----------|------| +| `RDEPUBReaderRuntime` | `ReaderRuntime` | Facade,协调所有子 Coordinator | +| `RDEPUBReaderLoadCoordinator` | `LoadCoordinator` | EPUB 文件解析 + Publication 初始化 | +| `RDEPUBReaderPaginationCoordinator` | `PaginationCoordinator` | 分页计算 | +| `RDEPUBReaderLocationCoordinator` | `LocationCoordinator` | 阅读位置管理 + 持久化 | +| `RDEPUBReaderChromeCoordinator` | `ChromeCoordinator` | 工具栏状态同步 | +| `RDEPUBReaderAnnotationCoordinator` | `AnnotationCoordinator` | 高亮/书签/批注 CRUD | +| `RDEPUBReaderSearchCoordinator` | `SearchCoordinator` | 全文搜索协调 | +| `RDEPUBReaderViewportMonitor` | `ViewportMonitor` | 屏幕旋转/尺寸变化检测 | + +### 5.3 WebView 内容视图 + +对应 iOS `RDEPUBWebView` + `RDEPUBWebContentView`: + +```kotlin +// EpubWebView.kt — 封装 Android WebView +class EpubWebView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null +) : WebView(context, attrs) { + + private val bridge = EpubBridge(/* ... */) + + fun setup() { + settings.javaScriptEnabled = true + settings.domStorageEnabled = true + addJavascriptInterface(bridge, "AndroidBridge") + webViewClient = EpubSchemeHandler(publication) + webChromeClient = WebChromeClient() + } + + fun loadChapter(spineIndex: Int, preferences: ReadingPreferences) { + // 生成 HTML 模板 + 注入 CSS + 加载资源 + } + + fun evaluateScript(script: String, callback: ((String?) -> Unit)? = null) { + evaluateJavascript(script) { result -> callback?.invoke(result) } + } +} +``` + +### 5.4 顶部工具栏 + +对应 iOS `RDEPUBReaderTopToolView`: + +```xml + + + + + + + +``` + +### 5.5 底部工具栏 + +对应 iOS `RDEPUBReaderBottomToolView`: + +```xml + + + + + + + + +``` + +### 5.6 搜索面板 + +对应 iOS `RDEPUBReaderSearchBarView`: + +```kotlin +// SearchPanelView.kt +class SearchPanelView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : LinearLayout(context, attrs, defStyleAttr) { + + private val searchField: EditText + private val cancelButton: Button + private val resultsList: RecyclerView // 分组结果列表 + private val emptyStateLabel: TextView + + fun updateResults(sections: List, keyword: String, currentMatchIndex: Int?) + fun showNoResults() + fun showSearching() +} +``` + +### 5.7 设置面板 + +对应 iOS `RDEPUBReaderSettingsViewController`: + +使用 `BottomSheetDialogFragment` 实现底部弹出设置面板: +- 亮度调节(SeekBar) +- 字体大小(+/- 按钮) +- 字体选择 +- 行间距 +- 主题切换(6 种预设) +- 翻页模式切换 + +### 5.8 目录列表 + +对应 iOS `RDEPUBReaderChapterListController`: + +使用 `BottomSheetDialogFragment` + `RecyclerView` 实现目录列表,支持章节标题显示和点击跳转。 + +--- + +## 第六阶段:持久化与状态管理(预计 0.5 周) + +### 6.1 持久化实现 + +对应 iOS `RDEPUBReaderPersistence`: + +```kotlin +// ReaderPersistence.kt +interface ReaderPersistence { + fun loadLocation(bookId: String): ReadingLocation? + fun saveLocation(bookId: String, location: ReadingLocation) + fun loadBookmarks(bookId: String): List + fun saveBookmarks(bookId: String, bookmarks: List) + fun loadHighlights(bookId: String): List + fun saveHighlights(bookId: String, highlights: List) + fun loadSettings(): ReaderSettings + fun saveSettings(settings: ReaderSettings) +} + +// SharedPreferences 实现 +class SharedPrefsPersistence(context: Context) : ReaderPersistence { + // 使用 Gson 序列化/反序列化 +} +``` + +### 6.2 状态管理 + +使用 Kotlin `StateFlow` 替代 iOS 的手动状态同步: + +```kotlin +// ReaderState.kt +data class ReaderUiState( + val isLoading: Boolean = true, + val currentPage: Int = 0, + val totalPages: Int = 0, + val title: String = "", + val isToolbarVisible: Boolean = false, + val isSearchVisible: Boolean = false, + val bookmarks: List = emptyList(), + val highlights: List = emptyList(), + val searchState: SearchState? = null, +) + +class ReaderStateManager { + private val _uiState = MutableStateFlow(ReaderUiState()) + val uiState: StateFlow = _uiState.asStateFlow() +} +``` + +--- + +## 第七阶段:主题与外观(预计 0.5 周) + +### 7.1 主题系统 + +对应 iOS `RDEPUBReaderTheme`: + +```kotlin +// ReaderTheme.kt +data class ReaderTheme( + val name: String, + val contentBackgroundColor: Int, // Color int + val textColor: Int, + val toolbarBackgroundColor: Int, + val toolbarForegroundColor: Int, + val isDark: Boolean +) { + companion object { + val LIGHT = ReaderTheme("浅色", Color.WHITE, Color.BLACK, ...) + val DARK = ReaderTheme("深色", Color.rgb(0x1A, 0x1A, 0x1A), Color.WHITE, ...) + val YELLOW = ReaderTheme("纸质", Color.rgb(0xF5, 0xF0, 0xE0), Color.BLACK, ...) + // ... 共 6 种 + } +} +``` + +### 7.2 状态栏适配 + +根据主题动态设置状态栏颜色(`Window.statusBarColor`)和图标颜色(`WindowInsetsController.isAppearanceLightStatusBars`)。 + +--- + +## 第八阶段:测试与 Demo(预计 1 周) + +### 8.1 单元测试 + +- `EpubParserTest` — EPUB 解析(container.xml、OPF、TOC) +- `SearchEngineTest` — 搜索引擎 +- `StyleSheetBuilderTest` — CSS 生成 +- `ResourceResolverTest` — URL 解析 +- `LocationCoordinatorTest` — 位置管理 + +### 8.2 集成测试 + +- `EpubReaderIntegrationTest` — 完整阅读流程(打开 → 分页 → 翻页 → 搜索 → 高亮) + +### 8.3 Demo App + +对应 iOS `ReadViewDemo`: + +```kotlin +// MainActivity.kt +class MainActivity : AppCompatActivity() { + private fun openBook() { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + type = "application/epub+zip" + } + startActivityForResult(intent, REQUEST_OPEN_BOOK) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == REQUEST_OPEN_BOOK && resultCode == RESULT_OK) { + val uri = data?.data ?: return + val fragment = EpubReaderFragment.newInstance(uri) + // 嵌入 Fragment + } + } +} +``` + +### 8.4 UI 自动化测试 + +对应 iOS 的 XCUIApplication 测试(24 个测试文件): +- 使用 Espresso + UiAutomator +- 覆盖:目录交互、页码导航、书签管理、高亮管理、搜索、设置效果、工具栏状态等 + +--- + +## 实施时间估算 + +| 阶段 | 内容 | 预计工时 | +|------|------|---------| +| 1 | 项目基础设施 | 1 周 | +| 2 | Core 层(解析、模型、搜索、CSS) | 2 周 | +| 3 | JS 桥接层 | 1 周 | +| 4 | 翻页容器 | 1.5 周 | +| 5 | UI 组件 | 2 周 | +| 6 | 持久化与状态管理 | 0.5 周 | +| 7 | 主题与外观 | 0.5 周 | +| 8 | 测试与 Demo | 1 周 | +| **合计** | | **约 9.5 周** | + +--- + +## 关键技术决策 + +1. **统一 WebView 渲染**:不实现 DTCoreText 等效层,所有内容通过 WebView + CSS column 分页。简化实现,与 iOS 的 webInteractive 路径一致。 + +2. **Coordinator 模式保留**:与 iOS 架构保持一致,便于两端逻辑对照和维护。 + +3. **JS Bridge 微调**:Android 的 `@JavascriptInterface` 与 iOS 的 `WKScriptMessageHandler` 机制不同,JS 端需要适配(用 `AndroidBridge.*` 替代 `window.webkit.messageHandlers.*`)。 + +4. **不使用 Kotlin Multiplatform**:当前阶段直接用 Kotlin 重写,避免 KMP 的额外复杂度。未来如果需要共享逻辑层,可以逐步迁移。 + +5. **ViewPager2 替代 PageViewController**:Android 无 page curl 效果,使用 ViewPager2 + 自定义 PageTransformer 实现翻页动画(可选深度效果或滑动效果)。 + +--- + +## 文件映射索引(iOS → Android) + +### 核心映射 + +| iOS Swift 文件 | Android Kotlin 文件 | 行数参考 | +|---------------|-------------------|---------| +| `RDEPUBParser.swift` + extensions | `EpubParser.kt` + extensions | ~600 | +| `RDEPUBModels.swift` | `EpubModels.kt` | ~200 | +| `RDEPUBPublication.swift` | `EpubPublication.kt` | ~150 | +| `RDEPUBJavaScriptBridge.swift` | `EpubJavaScriptBridge.kt` | ~400 | +| `RDEPUBStyleSheetBuilder.swift` | `StyleSheetBuilder.kt` | ~300 | +| `RDEPUBSearchEngine.swift` | `EpubSearchEngine.kt` | ~150 | +| `RDEPUBPaginator.swift` | `EpubPaginator.kt` | ~200 | +| `RDEPUBReadingSession.swift` | `ReadingSession.kt` | ~250 | +| `RDEPUBReaderController.swift` + extensions | `EpubReaderFragment.kt` + extensions | ~800 | +| `RDEPUBReaderContext.swift` | `ReaderContext.kt` | ~150 | +| `RDEPUBReaderRuntime.swift` | `ReaderRuntime.kt` | ~200 | +| 各 Coordinator 文件 | 对应 `*Coordinator.kt` | 各 ~100-200 | +| `RDEPUBWebView.swift` + extensions | `EpubWebView.kt` + extensions | ~500 | +| `RDReaderView.swift` + extensions | `PagerView.kt` + extensions | ~400 | +| `RDEPUBReaderSearchBarView.swift` | `SearchPanelView.kt` | ~500 | +| `RDEPUBReaderTopToolView.swift` | `TopToolBar.kt` | ~150 | +| `RDEPUBReaderBottomToolView.swift` | `BottomToolBar.kt` | ~200 | +| `RDEPUBReaderSettingsViewController.swift` | `SettingsSheetFragment.kt` | ~300 | + +### 直接复用(不需翻译) + +| 文件 | 说明 | +|------|------| +| `epub-bridge.js` | JS 核心桥接 | +| `WeReadApi.js` | JS API 门面 | +| `cssInjector.js` | CSS 注入工具 | +| `rangy-core.js` | Range 操作库 | +| `rangy-serializer.js` | Range 序列化 | + +### 不迁移(Android 不需要) + +| iOS 文件/目录 | 原因 | +|-------------|------| +| `EPUBTextRendering/`(18 个文件) | DTCoreText/CoreText 无 Android 等价物,统一用 WebView | +| `EPUBUI/TextPage/`(7 个文件) | 原生文字渲染层,Android 不需要 | +| `RDEPUBTextContentView.swift` | 同上 | +| `RDEPUBTextPageRenderView.swift` | 同上 | +| `RDEPUBWebDecorationOverlayView.swift` | Android WebView 的高亮由 JS 直接处理,不需要原生 overlay | diff --git a/Doc/当前阅读器问题修复开发清单.md b/Doc/当前阅读器问题修复开发清单.md index 802eaf6..452d6a2 100644 --- a/Doc/当前阅读器问题修复开发清单.md +++ b/Doc/当前阅读器问题修复开发清单.md @@ -1,6 +1,6 @@ # 当前阅读器问题修复开发清单 -> 最后更新:2026-06-12 +> 最后更新:2026-06-13 > 依据说明:本清单仅基于当前仓库代码实现整理,不以现有说明文档是否准确为前提。 --- diff --git a/ReadViewDemo/Podfile.lock b/ReadViewDemo/Podfile.lock index b90ca91..7ee89af 100644 --- a/ReadViewDemo/Podfile.lock +++ b/ReadViewDemo/Podfile.lock @@ -43,7 +43,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: DTCoreText: 11b7fe2104f476f82e75a4e3dbdde74d7186cecb DTFoundation: 76b624967cf5bcaae6bb057d622c536c36ef36d0 - RDReaderView: 617ec758a5db3c10024acf83b110465ea8343b5b + RDReaderView: 54205c82c62ea6c674b43c833642d2643c98c573 SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a SSAlertSwift: aad8dc0c20b36fcffe700b81d7be89d60c7ba4f1 ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351 diff --git a/ReadViewDemo/Pods/Local Podspecs/RDReaderView.podspec.json b/ReadViewDemo/Pods/Local Podspecs/RDReaderView.podspec.json index a095b92..76eb8d6 100644 --- a/ReadViewDemo/Pods/Local Podspecs/RDReaderView.podspec.json +++ b/ReadViewDemo/Pods/Local Podspecs/RDReaderView.podspec.json @@ -27,15 +27,9 @@ "ZIPFoundation": [ "~> 0.9" ], - "DTCoreText": [ - - ], - "SnapKit": [ - - ], - "SSAlertSwift": [ - - ] + "DTCoreText": [], + "SnapKit": [], + "SSAlertSwift": [] }, "requires_arc": true, "swift_version": "5.10" diff --git a/ReadViewDemo/Pods/Manifest.lock b/ReadViewDemo/Pods/Manifest.lock index b90ca91..7ee89af 100644 --- a/ReadViewDemo/Pods/Manifest.lock +++ b/ReadViewDemo/Pods/Manifest.lock @@ -43,7 +43,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: DTCoreText: 11b7fe2104f476f82e75a4e3dbdde74d7186cecb DTFoundation: 76b624967cf5bcaae6bb057d622c536c36ef36d0 - RDReaderView: 617ec758a5db3c10024acf83b110465ea8343b5b + RDReaderView: 54205c82c62ea6c674b43c833642d2643c98c573 SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a SSAlertSwift: aad8dc0c20b36fcffe700b81d7be89d60c7ba4f1 ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351 diff --git a/ReadViewDemo/Pods/Pods.xcodeproj/project.pbxproj b/ReadViewDemo/Pods/Pods.xcodeproj/project.pbxproj index 8f0ccb9..febdc0f 100644 --- a/ReadViewDemo/Pods/Pods.xcodeproj/project.pbxproj +++ b/ReadViewDemo/Pods/Pods.xcodeproj/project.pbxproj @@ -8,570 +8,571 @@ /* Begin PBXBuildFile section */ 002685407B9C4D548571DA24AFD6E8F1 /* NSString+Paragraphs.m in Sources */ = {isa = PBXBuildFile; fileRef = F53CCB60DE5D06A43E4A52EDB274E7D2 /* NSString+Paragraphs.m */; }; - 00B6BBA8B3A4551F0B349467EE60B357 /* DTAnimatedGIF.m in Sources */ = {isa = PBXBuildFile; fileRef = BC332335B380BC51C4F05C5CCDFB9189 /* DTAnimatedGIF.m */; }; + 00B5E59F71F71A71B769FD3DDBDAF965 /* RDEPUBTextPaginationInterfaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B791CDD0D79122F60EC136C025BC751 /* RDEPUBTextPaginationInterfaces.swift */; }; 014449AE560FFF3C628FAE639D043004 /* DTAnchorHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D63AE1801C7AB149219317BE7529B26 /* DTAnchorHTMLElement.m */; }; - 01D50E7898F9ECCFE0703106D7A12066 /* RDReaderView+ToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93898CAF537398E00D8113217C419CA9 /* RDReaderView+ToolView.swift */; }; - 02042C62EF343DD67D81BE25DC028830 /* RDEPUBPageInteractionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356DE2A2E8D844E60004E3AAB7B524FA /* RDEPUBPageInteractionController.swift */; }; - 025AE2757712862E5524EEDB2525C5CD /* DTHTMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FB2B1B0498DCBE7B79BDF49E963E3D /* DTHTMLParser.m */; }; - 0312BB93F623A40D79BA6B5610EF1C34 /* Archive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C8F0DCEAA5A3DF38C503CA30DC94C1C /* Archive.swift */; }; - 032C67D09A34F3EDFEFAA1B665F3D45A /* RDEPUBReaderBottomToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 778702DC79D5849F3C4DF790310D0917 /* RDEPUBReaderBottomToolView.swift */; }; - 035D32F76982E769321736F9AE645A39 /* ConstraintMakerRelatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD768C27E392C934C06DFB4AC401037C /* ConstraintMakerRelatable.swift */; }; - 0367DADEDFF60CD8EC0A571F521EA7B3 /* wxread-dark.css in Resources */ = {isa = PBXBuildFile; fileRef = C4C7097CA92DB08445EE4D912804A41B /* wxread-dark.css */; }; - 0393E3298DC788FD7C328C2CBB1ADD71 /* DTWeakSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = C422BCADBB48E28B549495663705A0FB /* DTWeakSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 014E4415B0D09135065AC238E6414DEA /* FileManager+ZIPDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F9B298989305B9569B71E448E8C1D4 /* FileManager+ZIPDeprecated.swift */; }; + 016371375864889F69517D55CDA3A266 /* RDEPUBTextSelectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2909100BDF5A0331D6BE22AF122BB95F /* RDEPUBTextSelectionController.swift */; }; + 021C7AEF8B20CD2494A1DB2BD22D0ABA /* RDEPUBReadingLocationModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CD5FB30A34088E10667AF4D2A8A6C3 /* RDEPUBReadingLocationModels.swift */; }; + 0348E85E071F1963286B90D704A584EB /* DTPieProgressIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B443D035BA5652B03A41BE905C5AC17 /* DTPieProgressIndicator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 03646B79FE3DB509B26BEF05E4E7C31F /* Archive+Writing.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF988440049800DB7824A94B81D20254 /* Archive+Writing.swift */; }; 03D90C831397E8636A33CE557024A136 /* NSMutableAttributedString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = A2C299CFB47AA6ECF86F7348E1EB751F /* NSMutableAttributedString+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0422D9463811398A8B8106DFC5151DAB /* RDEPUBPaginationModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B6B10413287246F7D530687A388D83A /* RDEPUBPaginationModels.swift */; }; + 043A2E9478F84524117CC70002023DFF /* RDPlainTextBookBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB57CE3D5558A2995D7CEF1CAD00079 /* RDPlainTextBookBuilder.swift */; }; + 04E3B722AA4ACECD835F6FEAB7127684 /* RDEPUBChapterLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8687FD6159F07520A97D89A4A739AB /* RDEPUBChapterLoader.swift */; }; 0561DF6793C8516F469D9813F5CE3211 /* DTCoreTextLayouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D6DA580F40DF113709E15CF6542A35C /* DTCoreTextLayouter.m */; }; - 05818B0A9C710EF83D4E1A525E87FA79 /* RDEPUBParser+Resources.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1A6EBFEF43855FDA02B545BAAC10BB8 /* RDEPUBParser+Resources.swift */; }; - 06A622DE7D129677B3A306A2D542DA86 /* NSData+DTCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 02EDFE0EEA47398F3E1A361A73AF6A6C /* NSData+DTCrypto.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 086D9D0020342C633405486DB9FFC40B /* RDEPUBTypesettingPipeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0B9221B1E7C6BE99BF31D7EA5C9AA94 /* RDEPUBTypesettingPipeline.swift */; }; - 095388DD719D03060DF1BBD554BD2FEF /* DTAnimatedGIF.h in Headers */ = {isa = PBXBuildFile; fileRef = A9FBC69C7DA29A7283920FDA9EBBCE8A /* DTAnimatedGIF.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 098496905EA0A142F47D4418FE7C767A /* Date+ZIP.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76A97683FB5E8E8B86EA4AD71F6F4B4 /* Date+ZIP.swift */; }; - 09E75B00712A3C1368E7DA2819A9906A /* ConstraintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A02EA0374C693F881AB36202FB276DCD /* ConstraintView.swift */; }; - 0A16E0D7CB8036C3C10A4F0A361ECB85 /* UIScreen+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F051D813D50D20D53205D5FDCAA877C /* UIScreen+DTFoundation.m */; }; - 0A5EB141623189C9CA54D72EFC964858 /* RDEPUBTextPerformanceSampler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5336F230E9CBA5516A0C4F48CC80B2 /* RDEPUBTextPerformanceSampler.swift */; }; + 0724227458D6158434EB3BA34EF75208 /* DTFolderMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 2101691D5743105B2E3E40376584B91C /* DTFolderMonitor.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 075B6A431606735FF10DCA4BA1599B61 /* SSAlertSwift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 937CEFA81DA4227008DBFB114783A08B /* SSAlertSwift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 07A7ADAEF645213B401323F269E7CC56 /* NSString+DTFormatNumbers.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ACA5E519F100582F0D772FCD76125C0 /* NSString+DTFormatNumbers.m */; }; + 086DCEC7AEBFD0464283CCEFAF12ADF0 /* RDReaderFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1DF1B2EEF04D6470AB3196636E73075 /* RDReaderFlowLayout.swift */; }; + 095FEA4761F0931E859306E016FE9D46 /* Archive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C8F0DCEAA5A3DF38C503CA30DC94C1C /* Archive.swift */; }; + 099BC8C8BFDA73D3998298527ACD794B /* ConstraintPriorityTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 882FA707ED3513D263A1E0656AAD2D33 /* ConstraintPriorityTarget.swift */; }; + 0A8A60254CCFFFB540A977E422004D68 /* RDEPUBWebView+JavaScriptBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E72A1A5E0D8524A2374DA24CDD8494 /* RDEPUBWebView+JavaScriptBridge.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, ); }; }; - 0BFB92A40ED6F693E9985B94C16BBA06 /* NSArray+DTError.h in Headers */ = {isa = PBXBuildFile; fileRef = CEA93EDA6ED54D374265AA5D6B9EBBA9 /* NSArray+DTError.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0D6C585E17F4576329E50CF07B85CE15 /* UIImage+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 15941683D394FB8D7EAE91CD2CF03FF1 /* UIImage+DTFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0D7A3666C06D8695D703BF8A93C467BC /* RDReaderView+PageCurl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587454250340EBB39CD77123D727F116 /* RDReaderView+PageCurl.swift */; }; - 0F0AB96A761DEDB65E66F94864C9C880 /* NSMutableArray+DTMoving.m in Sources */ = {isa = PBXBuildFile; fileRef = 53499F40E1790A6CBE8AFEB3AC5BEFC8 /* NSMutableArray+DTMoving.m */; }; - 0F1682ED32DDE83824C396E904963AA5 /* NSString+DTPaths.h in Headers */ = {isa = PBXBuildFile; fileRef = FC5EC7C3D1808C9CF53354D7A3D69EE7 /* NSString+DTPaths.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0F21BA3B21E2267D70F0B6285143BBE9 /* DTActivityTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EEFAE80193E1F0DA53400C9AD641410 /* DTActivityTitleView.m */; }; - 0F4E87E30789C356E774477D867F6F23 /* ConstraintMakerEditable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 392A75EAF280876545D14AA00517BA4C /* ConstraintMakerEditable.swift */; }; - 0F64292129E8BCF4B2D6E475A354FAA8 /* RDEPUBReaderController+PublicAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BB3C8634528BC5B2BC8EA70F1FD14FE /* RDEPUBReaderController+PublicAPI.swift */; }; - 0F668A5D4700EDA52CEEEF065C42BCA4 /* RDEPUBChapterWindowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0463606FFE0A076D81FC1ECDE5C47259 /* RDEPUBChapterWindowCoordinator.swift */; }; - 0FC6DE303CFDF04D3340415EABBA2B56 /* UIView+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4219E7C32019899890CAB66E5FBDF524 /* UIView+DTFoundation.m */; }; - 0FCBA3933EE0E5A478045C9C51B903E7 /* RDReaderPageChildViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC645BB4D42BB9762F362DF728F58946 /* RDReaderPageChildViewController.swift */; }; + 0BBE0292EC0E79F1D9548CDA1C22F2D8 /* RDEPUBTextAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E676DB9AB5DFC0FB4A33EC63DDACC0E8 /* RDEPUBTextAnchor.swift */; }; + 0D1F6D0CA56ABD2B42038C4088FB5BB5 /* SSAlertPresentAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E481007A8174943868EBC81CF3B04B9F /* SSAlertPresentAnimation.swift */; }; + 0D711A72DDF98852C2B1B0845AEB2E51 /* NSURL+DTUnshorten.h in Headers */ = {isa = PBXBuildFile; fileRef = 054674EB90C625BB4BEA9368ED92592B /* NSURL+DTUnshorten.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0DD55F7F8057B47C6B71DDD3EFDBBFEA /* DTAnimatedGIF.m in Sources */ = {isa = PBXBuildFile; fileRef = BC332335B380BC51C4F05C5CCDFB9189 /* DTAnimatedGIF.m */; }; + 0E3D5C67966CB30F8A1C5B23B32A78C6 /* DTBase64Coding.h in Headers */ = {isa = PBXBuildFile; fileRef = B8E40E302B85493BDE0FE91F829EC01E /* DTBase64Coding.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E4376280494B1718140645948A07E24 /* String+SHA256.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B03892CC4EE09B2E429D0F06E2296BD /* String+SHA256.swift */; }; + 0F1AA1FAA83F6E2D90B456429F5158AB /* ConstraintDirectionalInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91144FC099DD458E61625CD939577DAA /* ConstraintDirectionalInsets.swift */; }; + 0F3D7B798A409948DBC4C48F8B673679 /* ConstraintMakerPrioritizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E138EC76D0DF24544D29D1A4D7429E3 /* ConstraintMakerPrioritizable.swift */; }; + 0FAF20971B1067BD7F3005D2442D2A62 /* RDEPUBReaderLoadCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0D4092A12656EBCF1A6F72B4FD4082 /* RDEPUBReaderLoadCoordinator.swift */; }; 0FCBC8C64D8862F2A0E8794EDADDECBD /* UIFont+DTCoreText.h in Headers */ = {isa = PBXBuildFile; fileRef = 647FB0C5AAF84C71A7784DD277E48910 /* UIFont+DTCoreText.h */; settings = {ATTRIBUTES = (Public, ); }; }; 100BFFBD24F234C095EDB001B8088DB1 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = D1599698B5A62671C488F13E07272B09 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.m */; }; - 106B7BA59F8A3091A8AD453E71824882 /* Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0EBAC7714A5F73B75AAB47D24B08B71 /* Constraint.swift */; }; 109458F9D48FD4F381F0E40E2DB3D91C /* DTLazyImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = E5F69A1E1260BAD7737BEB533F643A15 /* DTLazyImageView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 109BE5337A3F49126904D50447B0A895 /* RDEPUBTextSearchEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8967AE46A1F72D8F08E5B81E1FE83ADA /* RDEPUBTextSearchEngine.swift */; }; - 10F77B4635FEE3110CC1A8B1670AC582 /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD081272B47A3B73CE693D21DC26827 /* Entry.swift */; }; + 10B2910F50DB63ED86D443844344308C /* RDEPUBParser+TOC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2353E8412F8D0EF503DEA9A6178F5948 /* RDEPUBParser+TOC.swift */; }; 11AB1902A68385851FB81E870991F9A0 /* DTImageTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = FEEB15F87BA6575BBFE1C5FD19B17CD7 /* DTImageTextAttachment.m */; }; 12B4A35B8E40C112E18A7C5511F023E9 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4472755F83C121DE5E1E505D145F6DBE /* CoreText.framework */; }; + 12C9A6EB078364B73087AFBC96A8AAF9 /* RDReaderSpreadResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7148B56DAB2824CB3E9E617789FD1908 /* RDReaderSpreadResolver.swift */; }; 130C3A85D9F9F445222C2E31EC245B0A /* NSScanner+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = A0927A875CA1F11DBCE60DFC6F0CA0B6 /* NSScanner+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 132602B42D91E7EBB0B757F6FCDB215B /* RDEPUBChapterCacheKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC7081E8A3F5A8A212D8F11DA4582929 /* RDEPUBChapterCacheKey.swift */; }; - 13C2DDE4E717A3E4CC202933AA8BDA50 /* RDEPUBReaderToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5921C735CE80E2A8BB3B05A1914CCC16 /* RDEPUBReaderToolView.swift */; }; - 14257DD9075535547195291198A4B56B /* RDEPUBReaderSearchBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BCA80CA6D8AE27898A0F352AA19FB31 /* RDEPUBReaderSearchBarView.swift */; }; + 139CC4381EDEA18EA5EA6FC4D3E62193 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEB14A8F8170777DB7CCC3F27C8EA57A /* ImageIO.framework */; }; + 1446BE1A7579119303BE0FE7D371DA14 /* DTFolderMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = B245F7E143F3091652C64F6593E33F7A /* DTFolderMonitor.m */; }; 14BDD9FB7A7AC5C8ABFF8D24C381F3DA /* NSAttributedString+DTCoreText.m in Sources */ = {isa = PBXBuildFile; fileRef = DBC9280BA00935565E9658775DD7404D /* NSAttributedString+DTCoreText.m */; }; + 14EAF9CBECC154CC00AACDD3224EC979 /* DTCustomColoredAccessory.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C6BE3B2DA9A8C4CD3760CDAD6F305A5 /* DTCustomColoredAccessory.m */; }; 15A7ED15DA03DD3B9F26A017D270D1C2 /* DTCSSListStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = FDD333B409D528E5006FA48F4D70A6F5 /* DTCSSListStyle.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 1649C327967B95DEB0F8E3E3315E5D81 /* RDEPUBBuildDiagnosticsReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7B78125ED71DFD5D7AB87570EE951A /* RDEPUBBuildDiagnosticsReporter.swift */; }; - 1733809C4DE2D2268981BB9421338594 /* UIScreen+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = A3765B289CB7E6C5DF6160D8EBC83C3C /* UIScreen+DTFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 17534667DC33BD803AE0B9652F9B8A9F /* RDEPUBWebView+Reflowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBD671BD778F560BB9C93218055756E1 /* RDEPUBWebView+Reflowable.swift */; }; - 17EC27F89C86E3BFDA6595D92C9B99DF /* RDEPUBStyleSheetBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C42316556BF2EEC09C00E6296E6B2D /* RDEPUBStyleSheetBuilder.swift */; }; - 18D86CDB39D4048E28706CE956113303 /* RDEPUBWebView+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9AB5C9C4F800F35807FC8C8C016D37 /* RDEPUBWebView+Search.swift */; }; + 15BB854C628E532A7F08FEA5594F3933 /* NSURL+DTComparing.h in Headers */ = {isa = PBXBuildFile; fileRef = 731C0C61E3AD227D691FBA1C2C17BF7B /* NSURL+DTComparing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1646DB61088E6EA55035448A0D00F9EC /* DTWeakSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = C422BCADBB48E28B549495663705A0FB /* DTWeakSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 16F6ACFCF57F6DAC2706790DA7242E9A /* NSDictionary+DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = C18480913E002315E3F5EC6945371697 /* NSDictionary+DTError.m */; }; + 172F60ACE858151BB15425047802D38F /* UIView+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4219E7C32019899890CAB66E5FBDF524 /* UIView+DTFoundation.m */; }; + 1755FA3D3F51158F1178A2788CBD2E3E /* NSFileWrapper+DTCopying.h in Headers */ = {isa = PBXBuildFile; fileRef = 09C957F3199C596781EFA1DEA1DDCEC8 /* NSFileWrapper+DTCopying.h */; settings = {ATTRIBUTES = (Public, ); }; }; 191824B2B1E1B0A7DD3B6298100B4B8A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE4821B98DDE6C71411EC139EC6D2762 /* CoreGraphics.framework */; }; - 194CC2A6339C6632772193C86071B332 /* RDEPUBTextAnnotationOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5416F4CC58991D4DFA213ED44B7AE12 /* RDEPUBTextAnnotationOverlay.swift */; }; 19649ED312D92A6F5D2D6AF4E540CE75 /* DTHorizontalRuleHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = E28CA95D89DBAB2FE56BC8513D5838D7 /* DTHorizontalRuleHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 19D0D49617A8039B4932A0784A647FC2 /* DTTiledLayerWithoutFade.m in Sources */ = {isa = PBXBuildFile; fileRef = 70F5D03B9B3B7F182C8F5C1F5D4FA7E7 /* DTTiledLayerWithoutFade.m */; }; - 1BB0F7065230323728EB5205C7E96E9E /* UIApplication+DTNetworkActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = 77213203802F44012346181FED2856F9 /* UIApplication+DTNetworkActivity.m */; }; - 1C08C84B9A7C1E693309BC43AA1E0063 /* RDEPUBReaderLoadCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6567762A90252DF61DBD13E3CCB5BF52 /* RDEPUBReaderLoadCoordinator.swift */; }; - 1C332AD59B3889359CBF39BA7BCECA50 /* RDEPUBTextBuildPipelineInterfaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31318B9A80CC694F9EFE2A72D4826B64 /* RDEPUBTextBuildPipelineInterfaces.swift */; }; - 1C59E564A3D3B215FDD80CB84FCF9B24 /* ConstraintMakerExtendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5245010774F8F1C4857763CC7B96E7B4 /* ConstraintMakerExtendable.swift */; }; - 1C82DEA8CBD206778DB821B9A05C83AE /* RDEPUBReaderSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6714862E1752548842941DFE880513C0 /* RDEPUBReaderSettingsViewController.swift */; }; - 1CF76515B052AD77C383F1A3F79F777A /* SSAlertAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D2EC0B845131A88247902AEA26F40AE /* SSAlertAnimation.swift */; }; - 1CF962A07C17085793B630F12C39D0AB /* RDReaderPreloadController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC152E542DB1CE18D5FB239C50E7DD27 /* RDReaderPreloadController.swift */; }; + 1BE56F06EBF770B5C5CC88E777A185DE /* RDReaderView+CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 579711B0A35AF68319DBD269278F32FC /* RDReaderView+CollectionView.swift */; }; + 1BEC1F32A7BA4DD1B31433C0A2F5B493 /* RDEPUBReaderController+TableOfContents.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6FE25CC16ECB8AB03FC9995C9074DB /* RDEPUBReaderController+TableOfContents.swift */; }; + 1C71EB408583CEAD67B9C214EC632173 /* rangy-core.js in Resources */ = {isa = PBXBuildFile; fileRef = DA5D38D76DC2B327F6FA86DD38AD5079 /* rangy-core.js */; }; + 1C9E8ABD096473C28D2986D3B32983B7 /* wxread-default.css in Resources */ = {isa = PBXBuildFile; fileRef = 752EEC76100E31C66D33F087A318A0B8 /* wxread-default.css */; }; 1D460603DCA4A4E5ECFE8FF1F1CACC10 /* DTColorFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = AFC4C9F69BF2D0CAEBAA83896C139D2C /* DTColorFunctions.m */; }; - 1D989BC9B2C48DF57ECCD9D275967ED1 /* RDEPUBSemanticMarkerInjector.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBD9F7B0D64CCCDACB7D703E942A4EBE /* RDEPUBSemanticMarkerInjector.swift */; }; - 1DB70691E03C930D4382CAEECA12A97B /* RDReaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7CBC59E42E8E03559CFEE4F14A56AC /* RDReaderView.swift */; }; - 1E90149260990A88EBA1EBDE5C03EA9C /* DTCustomColoredAccessory.h in Headers */ = {isa = PBXBuildFile; fileRef = 33DB57DF8C5AE334B1BBE9F989352F1D /* DTCustomColoredAccessory.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 1EAC8D7FE1D8B98841823C8F8E6BF856 /* NSURL+DTComparing.h in Headers */ = {isa = PBXBuildFile; fileRef = 731C0C61E3AD227D691FBA1C2C17BF7B /* NSURL+DTComparing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 1F919D0C4FAC68D245BBAE657EDCE900 /* RDEPUBModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 407122F95D1113F8C0E241B944F75AFE /* RDEPUBModels.swift */; }; - 1FB0FCC893FA187DBB7B49718B787AA8 /* RDEPUBTextContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 266AEC0C2D6983818D5C107DEA5BB286 /* RDEPUBTextContentView.swift */; }; - 20FF4D9CA3EC29F7B0E7F5A3FDE14A4A /* RDEPUBReaderController+ContentDelegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DC3AB46FAF0445813FD5AFA0E66194C /* RDEPUBReaderController+ContentDelegates.swift */; }; 2203356EEA94A9172BC7902923E43CBE /* DTCoreTextFontCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B7771FDE9AB1688166584C0AA03F12A /* DTCoreTextFontCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 221A6546C685365590F113AD63E50BC1 /* RDEPUBParser+Archive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1774BB2BD665DE754F86F839A89C4FBB /* RDEPUBParser+Archive.swift */; }; - 235CE890F1B1C5530A6D0ABF73AAAC66 /* ConstraintInsetTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A5ABB8F3656A110B955B0306E9A8C3 /* ConstraintInsetTarget.swift */; }; - 2404CA703443CC7DF42099D57B7846C7 /* RDEPUBTextIndexTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAF8513093523204F2598DC45941FB49 /* RDEPUBTextIndexTable.swift */; }; + 23159E3216F7755E8557DFDB8356B687 /* RDEPUBPaginator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4740BDFF4EED78481148A3109207228A /* RDEPUBPaginator.swift */; }; 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, ); }; }; + 24A4EC565C31C36075596144B8F44299 /* UIViewFrameExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C351514678D41C58869BD057D5483F /* UIViewFrameExtension.swift */; }; 24C5B52FD2AA9C49731DC6859ABA7CDE /* NSDictionary+DTCoreText.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C50CAB4241C141EF6EC9ED22AB3A7E8 /* NSDictionary+DTCoreText.m */; }; - 24F57F63118933065AC2E2E13BFFFDA6 /* ConstraintRelation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3593ADF561D422F73C8EABAD4A4F440 /* ConstraintRelation.swift */; }; - 2550FB3DC06D75BF7265CD7924DC1807 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; }; + 25D2035E54373625D7DDE374536A1A22 /* DTBlockFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = A0BD48DAF5AE9DB087F2552E99069709 /* DTBlockFunctions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 25DA73F3FE71BA016BE8F3673C677DCC /* RDEPUBWebView+FixedLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22BDEB6AC81168F4F3FE78ACCEC2A32D /* RDEPUBWebView+FixedLayout.swift */; }; 270C5C10028F0BC5A62548326CCB379B /* DTColor+Compatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = E196F56E68578181D5BDE787DE5C0F3A /* DTColor+Compatibility.m */; }; - 27326E81ED7191074A965F1906994720 /* ConstraintLayoutGuide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6BA0ECCB0ACF8580C3B5192CA9F11B /* ConstraintLayoutGuide.swift */; }; - 274577F301594F7C79997B43A939DEC9 /* SnapKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B10D3EC1701BBA4D69926915F942558 /* SnapKit-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2767D03964C6AD5DF426A37648BE2DCE /* RDEPUBFixedLayoutTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FD2E18DC68BCC843FF378E84642CE78 /* RDEPUBFixedLayoutTemplate.swift */; }; - 2823587BD92CCD62B4A002B4B61E391B /* RDEPUBResourceURLSchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF667FF45F147B7458680F3808B36A4 /* RDEPUBResourceURLSchemeHandler.swift */; }; - 28600C55542F98933CB6752E92E0B58F /* Archive+Writing.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF988440049800DB7824A94B81D20254 /* Archive+Writing.swift */; }; - 288D7FCEDD998F32DEBCB08A8FFB0DAE /* RDEPUBRuntimePageCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3785B10F33122FC204F180B9E2366069 /* RDEPUBRuntimePageCount.swift */; }; - 298DB0F0F244610847C472F016BF2676 /* DTTiledLayerWithoutFade.h in Headers */ = {isa = PBXBuildFile; fileRef = DF5EDCA3B21964D0462BE0982E2F0BF8 /* DTTiledLayerWithoutFade.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2818B9AC60001AB09015CD0BD5C9D339 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; }; + 28402E474AD3C0223488D3AF7C0D159B /* URL+ZIP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537BB06317FA188DF7A8A960152520E8 /* URL+ZIP.swift */; }; + 28E0E06D8E8774BE8861C32B9B48A80E /* RDEPUBPageCountCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B2963687ADF8375028F8C6F50B1F40B /* RDEPUBPageCountCache.swift */; }; 29D9B32DFFC59A157EDEF005DB92A5AC /* DTCoreTextLayoutLine.m in Sources */ = {isa = PBXBuildFile; fileRef = C71D83D6AEF198FA680B8A80BF2D9605 /* DTCoreTextLayoutLine.m */; }; - 29E60F8ECAC68269B4263A7DCCB0EF08 /* Archive+Progress.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD987C2FA7C8FBC96FD024E421DA0E68 /* Archive+Progress.swift */; }; 2AAF9361720FC2D8FFCA93B5FAE42019 /* NSMutableString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 31F3F91D5624C451E5D44D85B1DDE7C5 /* NSMutableString+HTML.m */; }; - 2BB6A5DF21E5E8AB6E5793A189746DC9 /* UIApplication+DTNetworkActivity.h in Headers */ = {isa = PBXBuildFile; fileRef = 957DB4675D5CCBD3CB1762F08619F3F7 /* UIApplication+DTNetworkActivity.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2BDA9B95A38F510B33098480B1CF2BFF /* RDEPUBWebView+JavaScriptBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E72A1A5E0D8524A2374DA24CDD8494 /* RDEPUBWebView+JavaScriptBridge.swift */; }; - 2DC6F25E5B1AC15B1041E5129E9DB5DC /* RDEPUBReaderController+DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F02953DC627CB32FFD38E101007BB9E /* RDEPUBReaderController+DataSource.swift */; }; - 2F25AF09403C69487847A7ADD6645B88 /* RDEPUBSearchEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82FE20C2FB684C45A8135A774F9EA2CA /* RDEPUBSearchEngine.swift */; }; + 2B0A0DE404D9534B91E22A587F11C05F /* ConstraintMultiplierTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BE22EE711B7A38A4443E1C1C12BD79 /* ConstraintMultiplierTarget.swift */; }; + 2BF71A220115480610FCD909D3F0C500 /* LayoutConstraintItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8C0D83EB6EDE71B264B6B0DD4644F5 /* LayoutConstraintItem.swift */; }; + 2D22916B3ADAA9CF78AF3CC5F40F2E9F /* RDEPUBBackgroundTrace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A71ED4237A07E81D0597E56658C5A /* RDEPUBBackgroundTrace.swift */; }; + 2D2EA7C7CEEE4FDB9B6E4E55D4C16B12 /* RDEPUBSearchEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82FE20C2FB684C45A8135A774F9EA2CA /* RDEPUBSearchEngine.swift */; }; 2FD7360250C2255A80583D4CF518FBFA /* DTTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = F35039A8D0AA1F7BAFC4E5E016057E15 /* DTTextAttachment.m */; }; 30C01FE491C26314F776AD8B2AF136FA /* NSAttributedStringRunDelegates.m in Sources */ = {isa = PBXBuildFile; fileRef = E6F2E79CC33E0823C4E719ECDDFBBE08 /* NSAttributedStringRunDelegates.m */; }; 31B805C50E0E187E6242B0C878C4D30D /* DTCoreTextMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5D34047BAA894BEF1DFA23E6A9DAB1 /* DTCoreTextMacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 31E359EFE25014EEF231198256A4A55D /* RDReaderPagingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D5362F7EC0A627ACAB9AF4CC7F51099 /* RDReaderPagingController.swift */; }; + 31FC7230D5C6B0306855EE5087D0A26F /* UIColor+RDEPUBHex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF05A515E551E5511DBE86C4A494EF1 /* UIColor+RDEPUBHex.swift */; }; + 31FFF99A325707F94A86A18AA48FF162 /* DTFoundation-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = FCC718115E5C31C88A680522AE36B089 /* DTFoundation-dummy.m */; }; + 3274F7034696EA627A4C0F934FB1D3B0 /* ConstraintLayoutGuide+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A9A82CB094636AFB53D6D130D6219BA /* ConstraintLayoutGuide+Extensions.swift */; }; 343E794F88AA503F5139C2244DC1A2B1 /* DTTextAttachmentHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = C298199F5DC5390E8B6666F638B34C10 /* DTTextAttachmentHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; 35C5BE59CA712CCDF93D6E7010D93883 /* DTAttributedTextContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = C123C127CC539ACCB510F665DB774899 /* DTAttributedTextContentView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 35F11E46B2193B801E96CE6CDDD4AE21 /* ConstraintMakerExtendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5245010774F8F1C4857763CC7B96E7B4 /* ConstraintMakerExtendable.swift */; }; 372E907DF08263D0E1AF904F1FCD66EB /* DTTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D36ED8209C907E3E9B074DDB399F54FF /* DTTextAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3736065BFCBF19E580DB5EC8FA17B884 /* RDEPUBReaderPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68C5ACCC99754FDAFCC9F78F9163AC5E /* RDEPUBReaderPersistence.swift */; }; - 37B41230C263BE09177B0FAD184D2B02 /* ConstraintDirectionalInsetTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = A651CEE8062C84A597DBC44ECA3DBCC1 /* ConstraintDirectionalInsetTarget.swift */; }; - 3800D19E7EC5920B171087AE11B25B31 /* RDEPUBReaderPaginationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E097E98410554175AFEAF5BF4CC963 /* RDEPUBReaderPaginationCoordinator.swift */; }; - 388702D454B856FB6B85047F469EA482 /* RDEPUBBackgroundTrace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6ABEFE84724594A5A9040F3C67BAFD3B /* RDEPUBBackgroundTrace.swift */; }; 389F7C8400F9223C019F2D7825CD2369 /* NSCharacterSet+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 713C9FD0354D16AFCCFA630A97F2DA68 /* NSCharacterSet+HTML.m */; }; - 38D07F9682C100D89684B0A57731BF21 /* RDEPUBReaderViewportMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F01C26E4C66501E3CEB74C752FA77D0 /* RDEPUBReaderViewportMonitor.swift */; }; - 3922A859DCEE60641247531983AF7749 /* RDEPUBWebViewDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B4DD7F34461088F19AB93FBE13CC9BD /* RDEPUBWebViewDebug.swift */; }; - 3A3FB95E5C631A93B0E25C0E9AFB3BF4 /* RDEPUBRenderRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44F8832CEF100545E84932BE0B82CA7B /* RDEPUBRenderRequest.swift */; }; - 3AAF769EB8C7417ADBF6CFC3D622B2FE /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEB14A8F8170777DB7CCC3F27C8EA57A /* ImageIO.framework */; }; - 3BB335D962947DC13E7352C7CEB56139 /* Entry+Serialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F991240340FA026C71B68EB25B9F9C8 /* Entry+Serialization.swift */; }; - 3BE1CD7DD55BECCC7C5DE5F962DD020A /* RDEPUBReaderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CBD2948DE6941D36BAF37A94CC7981A /* RDEPUBReaderController.swift */; }; - 3C5BD179D2489DD86A9B13D20971070B /* Data+CompressionDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D641809A99849F80FDCB02F8CE8D6A0 /* Data+CompressionDeprecated.swift */; }; - 3C9559FB7926249C99C5925FA6A8A71F /* default.css in Resources */ = {isa = PBXBuildFile; fileRef = 05D57D16346FAF830F1F82067307EA9F /* default.css */; }; - 3D2DAB8E64826A368B9284D8640322EC /* RDEPUBReaderController+TableOfContents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D44639116A69F6C16EC09917C13DD18 /* RDEPUBReaderController+TableOfContents.swift */; }; + 394F47614B158E6EB298F25D0B17E289 /* RDEPUBSemanticMarkerInjector.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBD9F7B0D64CCCDACB7D703E942A4EBE /* RDEPUBSemanticMarkerInjector.swift */; }; + 3C037B1C09894A3F5F84B3496DD6849A /* UIScreen+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F051D813D50D20D53205D5FDCAA877C /* UIScreen+DTFoundation.m */; }; + 3D6535403367C804D54FFD001A77E284 /* RDEPUBReaderPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F861867F08812A37E8DDCBB02DC583 /* RDEPUBReaderPersistence.swift */; }; 3DC8F9474FC6A0BB7E81A703CE958966 /* NSAttributedString+DTDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 71E2D85A099C99CD82FD1842EE7C52D2 /* NSAttributedString+DTDebug.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4043D3783C54C47FD0481A4A1B55247F /* RDEPUBChapterLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDB56AC5412CF16FDBB8BF9A606E9F1 /* RDEPUBChapterLoader.swift */; }; - 40AC0BE626808D9D04B82C8F334BC2BC /* RDEPUBReaderChromeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10AF8170B881EC5CFA3C1CA4AC3E27F1 /* RDEPUBReaderChromeCoordinator.swift */; }; - 41ACA47BF5AB95B16FC884D6AE5370AB /* wxread-replace.css in Resources */ = {isa = PBXBuildFile; fileRef = 4C61D04931F7736E429371D872F7E736 /* wxread-replace.css */; }; - 43F2F5FDA2FC0C6E0F25EAECA626C6BF /* ConstraintItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28D62D3200AC3D4E42C81CDFF7E884E8 /* ConstraintItem.swift */; }; - 4434DE1DB7F17FE76CC72C9F297FBB31 /* rangy-serializer.js in Resources */ = {isa = PBXBuildFile; fileRef = 14423BDEA85698D4A942B9C5F4BA2E17 /* rangy-serializer.js */; }; + 3E9CF41E75FE8F0F6DF4616796C901D5 /* RDEPUBNavigatorLayoutContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B36F32273C511FB9B6C51CE36A6DD9 /* RDEPUBNavigatorLayoutContext.swift */; }; + 3EC3CB613530B751AEB30443E1C5EEEE /* RDEPUBChapterData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D1621642B6C2702FFE6606483DEF37 /* RDEPUBChapterData.swift */; }; + 3ECDB3138484C8DD487C06D90F3764DB /* RDEPUBTextBookCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B3FF18F25CAE9935ED05E0856F20C3 /* RDEPUBTextBookCache.swift */; }; + 3EEA60E7A6E77DE25289208233488561 /* ConstraintConstantTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F090A0289338B14050FAF16C22605B0 /* ConstraintConstantTarget.swift */; }; + 3F431D135B47DE178B3D4D4E737B3F8E /* RDEPUBAttachmentNormalizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091717314CB1F573F5EE5EEC4DF86BA9 /* RDEPUBAttachmentNormalizer.swift */; }; + 3F47CD5028C4FAFCB02D90173FD7BE13 /* RDURLReaderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E83C08D0923B31898A62D63C06543BCC /* RDURLReaderController.swift */; }; + 3F59AAA6847628B5D101968C5B0FC0E2 /* RDReaderGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5CD25E951901E42A8D31765763F1CED /* RDReaderGestureController.swift */; }; + 3F7362665348377483660CCEE7E5A72E /* ConstraintViewDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E5637603691B9B35C2A886B686D3CE7 /* ConstraintViewDSL.swift */; }; + 400BA5D4228B97F4DE8199A4CABFFDFA /* RDEPUBReaderAnnotationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88DC78A6C33BEF2BC4ADB375F162C284 /* RDEPUBReaderAnnotationCoordinator.swift */; }; + 408741E5F917D3C08E31E4AD1A6136F7 /* ConstraintItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28D62D3200AC3D4E42C81CDFF7E884E8 /* ConstraintItem.swift */; }; + 4135416AE5B0306A47A967018A2CB7B8 /* Archive+ReadingDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934F9BB2B3A313294F121484C3A97A14 /* Archive+ReadingDeprecated.swift */; }; + 4204DDCF94EF5E57BDB32A10B8EC1BBD /* RDEPUBReaderController+RuntimeBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0813D235A2C7FFD9AC3F254EFC9F5FFC /* RDEPUBReaderController+RuntimeBridge.swift */; }; + 42761D3507E4C730BDBDDA969D0BD369 /* RDEPUBSelectionOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A0D2BE0587C3DDC57DDBDCD73F81BD /* RDEPUBSelectionOverlayView.swift */; }; + 42D25E859E8E34B7837070F521533223 /* RDEPUBTextLayoutFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD2D83120D70130690765D762EE062E /* RDEPUBTextLayoutFrame.swift */; }; + 437E6DEFBB36398B5CF02CEF30E5AB96 /* NSString+DTPaths.h in Headers */ = {isa = PBXBuildFile; fileRef = FC5EC7C3D1808C9CF53354D7A3D69EE7 /* NSString+DTPaths.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 43910F0EBE4E629A3479D75EFF08FE3F /* ConstraintMakerEditable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 392A75EAF280876545D14AA00517BA4C /* ConstraintMakerEditable.swift */; }; + 43DB7BD38DD98A921E1886DFB837B388 /* RDReaderView+ToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93898CAF537398E00D8113217C419CA9 /* RDReaderView+ToolView.swift */; }; + 446C758763501BB1E1EC07D962A7294E /* RDEPUBChapterWindowSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C56140C935C7B3F792647614D702DAB /* RDEPUBChapterWindowSnapshot.swift */; }; 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, ); }; }; + 4550FCF88CC84EA71FA0A7AF67CC2B22 /* ConstraintMaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60657D08CCDB38BFED30BD40F4720CE4 /* ConstraintMaker.swift */; }; 45F90B98546FDBA03AB112DFE859A185 /* DTCSSStylesheet.m in Sources */ = {isa = PBXBuildFile; fileRef = E0D90EB06FC37D534934542F9CAB1D74 /* DTCSSStylesheet.m */; }; - 46BD36B7EB1CBF0BB1E4AD4C84B516F4 /* RDEPUBRuntimeChapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05DFBF4E36FCC77E8BE2B51DF47B7F33 /* RDEPUBRuntimeChapter.swift */; }; 46EF32D586FF590CE2C60C66364B9357 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEB14A8F8170777DB7CCC3F27C8EA57A /* ImageIO.framework */; }; - 46FE133228B81200750001BFACC79E89 /* ConstraintDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6578DFAB51436E8DF266FD087700E26B /* ConstraintDescription.swift */; }; - 472C6BD678C2FCD8E50E4D9CD00B74E8 /* ConstraintViewDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E5637603691B9B35C2A886B686D3CE7 /* ConstraintViewDSL.swift */; }; - 4741B2B966F91956089CADFF6C1D8F21 /* DTBase64Coding.h in Headers */ = {isa = PBXBuildFile; fileRef = B8E40E302B85493BDE0FE91F829EC01E /* DTBase64Coding.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4847AAA31E7027FA8A0CEF293EDFB284 /* NSMutableArray+DTMoving.h in Headers */ = {isa = PBXBuildFile; fileRef = B0AC307F12521444C973EA0DE38B4608 /* NSMutableArray+DTMoving.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 484AEB14DBB75CA2E81B69C788F0C20A /* Archive+BackingConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61E36C4FE448DDDA03450847FCB785B6 /* Archive+BackingConfiguration.swift */; }; - 4948AB438B6A2316B0137CFDEE0E30CF /* SSAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F410ED25E4E84C3D5166261CDF4B58F2 /* SSAlertView.swift */; }; - 498305D5FF380F875B4FCF28218E2FED /* ConstraintRelatableTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885653999BD237A574B67DB279DE7E87 /* ConstraintRelatableTarget.swift */; }; - 49B23C37C1793A7BBD73EB21CD9A1F82 /* RDReaderView+ContentAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = D079A146A6FF29F12B0747422C2EA97F /* RDReaderView+ContentAccess.swift */; }; + 4784055F4929C8C6DD754D3C7862D686 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; }; + 48371ADBC5383DCCBE6E3F92DD59C6B1 /* ConstraintLayoutSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50297AAB8278F2BB6FCFE82B20C7A049 /* ConstraintLayoutSupport.swift */; }; 4A13B4E5EA8616A8F09D90BC6EAEEA64 /* CTLineUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = D24FE8634A7AD2562DB8ADB9D4BBB36A /* CTLineUtils.m */; }; - 4C2E3E6443766A6673F6913B9EC97D38 /* RDEPUBTextRendererSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B3FB8E803FAC9694F24527C90EFBB6 /* RDEPUBTextRendererSupport.swift */; }; - 4CF85C5575F21F66126226F1A54A939D /* Archive+ZIP64.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5693BD171B879406EA14C97F4AD8D8 /* Archive+ZIP64.swift */; }; + 4B2FDE4A4976B62F9FCE6CE8BC1AEE94 /* Data+CompressionDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D641809A99849F80FDCB02F8CE8D6A0 /* Data+CompressionDeprecated.swift */; }; + 4B4850CFF64032221BB0C2FBEFB7DD3E /* RDEPUBAnnotationModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39816EFB8DFB4FA6B91D9C8AB03A1C6A /* RDEPUBAnnotationModels.swift */; }; + 4BDE693D22EC8242C611167E4B09D538 /* ConstraintView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DB34ACB132E465CDF8191F7DB71EF6 /* ConstraintView+Extensions.swift */; }; + 4CAD70BBE240848A64180F7B1A0DCD8C /* WeReadApi.js in Resources */ = {isa = PBXBuildFile; fileRef = 017344CEAEFEB304A8BBF76EDF12D835 /* WeReadApi.js */; }; 4DA6EC46F8968265B96C6D7B2260C4E5 /* DTTextBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = ADC5AC8FD4EC45E9BA5654A38715BB93 /* DTTextBlock.m */; }; + 4DEA8CEF63598961120FBE983CD3E18B /* RDEPUBStyleSheetComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3DD09EADFE152D5050A931432C2C60 /* RDEPUBStyleSheetComposer.swift */; }; + 4DF5649A365CA463B70B022CBC72E8D3 /* RDEPUBFragmentMarkerInjector.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4CF544288387FDB24F07552DDE550 /* RDEPUBFragmentMarkerInjector.swift */; }; + 4E30F0CCCE719F18008FBA8F0A50FBEA /* NSString+DTURLEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 31BD7533867336DEA9B835F471767178 /* NSString+DTURLEncoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4E4939F6592B06D2CA764DCACFB47871 /* NSString+Paragraphs.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E37DC74BB53F90A5AD54842858A62F /* NSString+Paragraphs.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4EB15D9BDFFCDFE35C6FC81E71C761CD /* RDEPUBParser+Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D4CD395C5569628D36C4D838AC6A8B8 /* RDEPUBParser+Package.swift */; }; 4F50C703427747B88405080B221349A1 /* DTLinkButton.h in Headers */ = {isa = PBXBuildFile; fileRef = D7B5E61166782ED8D585D4FCEE2074DB /* DTLinkButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 504A9738EBFE1D9E4DC0023D92AE8F73 /* RDEPUBChapterSummaryDiskCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F3C3B060FF4C0BF31718EDE2DB45BB9 /* RDEPUBChapterSummaryDiskCache.swift */; }; 5065D99E1588E8CC60677E74916A5C03 /* DTAttributedTextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E1F63A854BBF28E93AC1D3F71CD48CBE /* DTAttributedTextCell.m */; }; - 50CA9421DFFA956A2659DFB804B142AF /* Archive+MemoryFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174FC0CA05D20BB995E211DE5A0857DB /* Archive+MemoryFile.swift */; }; - 512F03D47C95781C8187E080E49177B0 /* SSAlertAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97995C73297871C353364E685939DED9 /* SSAlertAnimationController.swift */; }; 51A1B5800D23C355B1712E8D1103780C /* DTTextBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = D497C3CA364E37DAAFF2C3B90CC7DBE8 /* DTTextBlock.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 528A1DE513701F24FD9B98742CB19706 /* RDEPUBFixedLayoutTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FD2E18DC68BCC843FF378E84642CE78 /* RDEPUBFixedLayoutTemplate.swift */; }; 528DBAC7FCEFC7D6575C1F6CB4399AEA /* DTStylesheetHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 96FA99AA7D4AD0D4246C8DAC1CC0C24E /* DTStylesheetHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5358DCB22018E30C798BEF35B60B1818 /* RDReaderGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5CD25E951901E42A8D31765763F1CED /* RDReaderGestureController.swift */; }; - 53FD0BA15BAA579860ADB13511BB470A /* RDReaderView-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = A09C96468ACCEB23603142794D15D1FA /* RDReaderView-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 549B5989FE7A52A0E861D46E2450F118 /* ConstraintPriorityTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 882FA707ED3513D263A1E0656AAD2D33 /* ConstraintPriorityTarget.swift */; }; - 54B29D0C9C457A01271023D91E5BFDD4 /* Data+Serialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC78D0B0F37BF9AD1058CACA9AAF9BE /* Data+Serialization.swift */; }; + 52978358028473BE63B6B1F6AA20675C /* DTBase64Coding.m in Sources */ = {isa = PBXBuildFile; fileRef = 061BD3911E4E530C792E65CED7868C33 /* DTBase64Coding.m */; }; + 546FA40FF62C090A815E63274A47A36C /* RDEPUBReaderController+DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04B186DAA39C121040AC3B1018D12E8 /* RDEPUBReaderController+DataSource.swift */; }; + 54B7A61A6E6A76A14DD71273E5E6B101 /* SSAlertAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97995C73297871C353364E685939DED9 /* SSAlertAnimationController.swift */; }; 5582735073EBB9F5078E6E8AEEFB646A /* DTCoreTextLayoutLine.h in Headers */ = {isa = PBXBuildFile; fileRef = 64AB89EAB10148C39B726B26BE04F4F8 /* DTCoreTextLayoutLine.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 55B8D3CC4726C45665A8A5ADCB0A7F79 /* RDEPUBReaderTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2687A6F390F43603F456693C4D2D90 /* RDEPUBReaderTheme.swift */; }; - 55F9FC052EC79F1184E3D8108AEF2648 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; }; - 5633438CD9B1C71CC74EE0A85717E313 /* RDEPUBReaderChapterListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74BFC8E9133BF4E551E3A93798B4F99 /* RDEPUBReaderChapterListController.swift */; }; + 55DA9AC6E71D27CF7C8D4FE3A247BD0B /* RDReaderView+ContentAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = D079A146A6FF29F12B0747422C2EA97F /* RDReaderView+ContentAccess.swift */; }; + 5636D02C578B36C6C52095DEEE012662 /* SSAlertViewExtention.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2280F2302F54E90122747821FE681543 /* SSAlertViewExtention.swift */; }; + 567A0978AA797E4385F13B23465F3585 /* Archive+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2659DB0685E16ABAE0382F726DA8C8 /* Archive+Helpers.swift */; }; + 567FA56050A61292B9224271986EF937 /* DTFoundationConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD568D38C209BA1CC0EBFA809A35204 /* DTFoundationConstants.m */; }; + 56921F586FA68A02088A7BFFB1B05319 /* RDEPUBPaginationModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B6B10413287246F7D530687A388D83A /* RDEPUBPaginationModels.swift */; }; 57D5228C8A85628772D4D78E7FBEAF3C /* DTDictationPlaceholderTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = 477A0FC2A1F0B7F7CCF72D201ECEBAB0 /* DTDictationPlaceholderTextAttachment.m */; }; + 593CA36C8608F7B33BEFB64959449B76 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; }; 597DE4CDF5C674E269F03F6AA0AFFCC1 /* NSAttributedString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 92460D196C5300656C9649784EBD3C58 /* NSAttributedString+HTML.m */; }; - 59A52D278576FC8C906A64F15F5530B3 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 1E89FE5F2996336E862E82C1E14E0EE7 /* PrivacyInfo.xcprivacy */; }; - 5A2947B6387888CB9CACE6A670ECDB45 /* ConstraintAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9593E7B37569219F8D9A504B47707DC /* ConstraintAttributes.swift */; }; - 5B26943069B0F5425BBE23E5A3F8F30A /* RDEPUBReaderContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB66A78848A8F3845FD918AEA8647871 /* RDEPUBReaderContext.swift */; }; - 5BAD2BFBCB3FB2505892A5A1480A7225 /* RDEPUBTextRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59AB14F9464EEFD112E2C56DF09B002D /* RDEPUBTextRenderer.swift */; }; - 5C537C41B6A15AC9F87DA0CD3F99B362 /* RDEPUBAssetRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CCFA3D24C3438DC554760F08D6E8281 /* RDEPUBAssetRepository.swift */; }; + 5A8E43AB3D398143BB25F39B222A18FE /* SSAlertAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D2EC0B845131A88247902AEA26F40AE /* SSAlertAnimation.swift */; }; + 5B268BA451F57A14CBB953F3CC488566 /* Archive+Reading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0619F98DA80922E78A07321D0DC54F62 /* Archive+Reading.swift */; }; + 5B38E03FBC5FA066303609D347C9406B /* RDEPUBReaderUIState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C106392FA85206E0EE3E17BDDEFF91 /* RDEPUBReaderUIState.swift */; }; 5D03A5B9DC067875CFEC9D4EA7B059E8 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = D863AE4F659A0F71B22DC9FF334DDF4E /* DTCoreTextLayoutFrameAccessibilityElementGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5D701F265B0D067A5441F932FD23056D /* DTFoundationConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD568D38C209BA1CC0EBFA809A35204 /* DTFoundationConstants.m */; }; + 5D775F2253E759A5B5FDA63EDD10E725 /* LayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932FD688DE493323BA6B691BC2CB5094 /* LayoutConstraint.swift */; }; 5D8D65121E87E419D8D2F23F8AF43673 /* DTColor+Compatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 80B3D15ED0D19D020663EF055386291A /* DTColor+Compatibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5DA72BD45B149DB685C7991AB6BC217B /* DTActivityTitleView.h in Headers */ = {isa = PBXBuildFile; fileRef = 8272D0B59ABD6652D0CAE3DFCC2C5EE1 /* DTActivityTitleView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5DFA66DC2F6B8ACE4D9C01A8DC268196 /* NSFileWrapper+DTCopying.h in Headers */ = {isa = PBXBuildFile; fileRef = 09C957F3199C596781EFA1DEA1DDCEC8 /* NSFileWrapper+DTCopying.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5D9E814BF6DF6BCE6842249F8A93DBC2 /* UIApplication+DTNetworkActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = 77213203802F44012346181FED2856F9 /* UIApplication+DTNetworkActivity.m */; }; + 5DB252F686F6F535F3B531EF2E60692E /* ConstraintConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7176B677A350927FC670368313DC2CA1 /* ConstraintConfig.swift */; }; + 5DC5386A66481FFAFAC58DA005F1B5FA /* RDEPUBPaginationCacheCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2883B11ACE6B06556D1B0FC8DC0F7E30 /* RDEPUBPaginationCacheCoordinator.swift */; }; + 5E82688424A62DA1557419401D47B340 /* Archive+WritingDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C7359A7C3FC7675FED220200A43A17 /* Archive+WritingDeprecated.swift */; }; 5E90EBD876ED4E50AB890FB56C4BFA57 /* DTHTMLParserNode.h in Headers */ = {isa = PBXBuildFile; fileRef = C8343CDCF82729A8EBCCA37A6650E34F /* DTHTMLParserNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5F6EDC70A6A29FD65F297DA272CEB1FD /* DTFoundation-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 5FAB3C7F19A511C2B1B7DFBA3CA308C4 /* DTFoundation-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5FE984F0E5484F951ADC5A6918DAE7CA /* RDEPUBReaderViewportMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DE6D61DD1D70A6DDF12430979C4082A /* RDEPUBReaderViewportMonitor.swift */; }; 60532A2E06CCFF814933D2FAE6793AE9 /* DTCoreTextLayouter.h in Headers */ = {isa = PBXBuildFile; fileRef = C26DBE1150BC9A30B69839783E000358 /* DTCoreTextLayouter.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 607E7C43D068045DA32EAA3E0B947305 /* NSURL+DTUnshorten.m in Sources */ = {isa = PBXBuildFile; fileRef = C80065DBA5634DAA71129404975D7349 /* NSURL+DTUnshorten.m */; }; - 6093C9FC371FB067097DD5D0D6731DFC /* UIViewFrameExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C351514678D41C58869BD057D5483F /* UIViewFrameExtension.swift */; }; + 609C3002E7FFCE826AEFFCE8805CDB49 /* RDEPUBReaderPaginationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E088FB8503A3AEBBB051817E55410CAE /* RDEPUBReaderPaginationCoordinator.swift */; }; + 616A164035FC6A7F5FD132B83DE46596 /* UIApplication+DTNetworkActivity.h in Headers */ = {isa = PBXBuildFile; fileRef = 957DB4675D5CCBD3CB1762F08619F3F7 /* UIApplication+DTNetworkActivity.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6179B2F08A02BFAEBE23834658BE70F3 /* ZIPFoundation-ZIPFoundation_Privacy in Resources */ = {isa = PBXBuildFile; fileRef = CA4A114775B98CEA4A566C18123B145F /* ZIPFoundation-ZIPFoundation_Privacy */; }; 61BE3607EABD2657BC71AF8ACB1C25D4 /* DTHTMLAttributedStringBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B59C9A1B265393E4BA8383B308A5917 /* DTHTMLAttributedStringBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 62567CA27B6255EA5850BB5CEC9B7543 /* LayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932FD688DE493323BA6B691BC2CB5094 /* LayoutConstraint.swift */; }; - 6284B2D1444FFA41C6E54990657E576B /* NSURL+DTComparing.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BFCAC7C2AEA867E67C63A852576774F /* NSURL+DTComparing.m */; }; - 6438FB9A11267C525E9F8A5E2A077C33 /* Typealiases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 945D4549E0B9168D7DDB910DE874B874 /* Typealiases.swift */; }; - 65002F5F1273AF0CBE8367B073F8661B /* ConstraintOffsetTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F950CEF31AC0065C0BEAB26BA2CEFA1 /* ConstraintOffsetTarget.swift */; }; + 627E8C3508550607CB5A183DFA7CBEF1 /* RDEPUBReadingSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26A9B74B4E5F952B9DC79C10B5D8E7F0 /* RDEPUBReadingSession.swift */; }; + 630146BD032D6366A273E5F74C33A863 /* RDReaderViewProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3653FA7AD3EF76156E4FB425AC90E5F4 /* RDReaderViewProtocols.swift */; }; + 6359AC2D2AA5C85B22CDBF3B961FEFB1 /* UILayoutSupport+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C647A2EB2720BCBF88B160D124B57BF /* UILayoutSupport+Extensions.swift */; }; 6634D568E49F5610242E1454680E5885 /* NSAttributedString+SmallCaps.h in Headers */ = {isa = PBXBuildFile; fileRef = DD189DA6054AAE2242F6644A2623B50E /* NSAttributedString+SmallCaps.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 6649710CC9FB92C9A52A3CAC70A2C9A0 /* Archive+WritingDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C7359A7C3FC7675FED220200A43A17 /* Archive+WritingDeprecated.swift */; }; + 674070237CC3D006DF6EB1F445C16362 /* RDEPUBReaderContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12109749AFDE57CACC5E56AB63AD3EB2 /* RDEPUBReaderContext.swift */; }; + 67525230780E71D60797BD57F84ACCD2 /* RDEPUBChapterCacheKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91EAE48AF0DC04CF719A938AD5613EC3 /* RDEPUBChapterCacheKey.swift */; }; + 67975174935DF5A3D66C101151239252 /* UIImage+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 15941683D394FB8D7EAE91CD2CF03FF1 /* UIImage+DTFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 67AE2AC8D9B961D45871077774E11FD3 /* DTObjectTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 073A7F9DF1A554BD1B324EA9E0090E91 /* DTObjectTextAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 68BB5D9462ECD306DC249D2F65983016 /* DTSmartPagingScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 96EEB6B022E22A4ABC86D87469D4815B /* DTSmartPagingScrollView.m */; }; - 6917152800EF84858313CDAE5EB2A480 /* RDEPUBParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CE0D7B6DB4A2FC4CFBD7F402AD7F6FA /* RDEPUBParser.swift */; }; + 67AE6029E724432408917C8A69CC4589 /* UIView+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 79A77584AB3B92B2508AE0BCFCB8FD87 /* UIView+DTFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 684C12A054C3831A1E74B0C9EEE7AE33 /* RDEPUBResourceResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E4EDBF01B37189D38063CF62178D33 /* RDEPUBResourceResolver.swift */; }; 69674FEA70899F07BE6098AAEC849978 /* DTAccessibilityViewProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 43CFADE0F46014990052AD67F17C86FF /* DTAccessibilityViewProxy.m */; }; - 69A26CBC7128559C74FA1467D2F97B65 /* Entry+ZIP64.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345CE45F696634CEA692A86D16F684C /* Entry+ZIP64.swift */; }; - 69D65E345F338AD1CF34B342CBC8B986 /* ZIPFoundation-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A06A36ADBE678DB44158D32E751FAFF /* ZIPFoundation-dummy.m */; }; - 6AB3FDA2AFB7EE7AAFDED21DB7DC0675 /* UIColor+RDEPUBHex.swift in Sources */ = {isa = PBXBuildFile; fileRef = C68BF07B565C3B6919A14BD1973D2AEE /* UIColor+RDEPUBHex.swift */; }; - 6AB69D332406B3820CA8165641F52C90 /* RDEPUBDTCoreTextRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2858C4311A2E3D5C75501279AD0FFE5 /* RDEPUBDTCoreTextRenderer.swift */; }; - 6C000EA66E8FA9A388BA73E62C421EAB /* SSAlertCommonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE4EE750340347ED584C5A2C1164331A /* SSAlertCommonView.swift */; }; + 6A8605EAC81CC09EC9AD3FA8D5CBCB76 /* RDReaderPagingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D5362F7EC0A627ACAB9AF4CC7F51099 /* RDReaderPagingController.swift */; }; + 6AD0EA5E10820A6B99B38A511DEF6F12 /* RDReaderView+PageCurl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587454250340EBB39CD77123D727F116 /* RDReaderView+PageCurl.swift */; }; + 6B95FC01A9B50EC9A6276642AF561CE2 /* NSArray+DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = 73940D215D644B6815A09DA802CFBDDF /* NSArray+DTError.m */; }; + 6C874FE3A64F7F50F191A64CC2BD543F /* RDEPUBBookPageMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4D1E00E7B5F60E25B326CF4B83E22B2 /* RDEPUBBookPageMap.swift */; }; + 6D6C04E917D89FF0D1B06C07134A7FB4 /* RDEPUBReaderHighlightsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E295867ADB067E27E538BB6802A15F65 /* RDEPUBReaderHighlightsViewController.swift */; }; + 6D893C2900F7DE1C4FD1790FFDDCE585 /* Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0EBAC7714A5F73B75AAB47D24B08B71 /* Constraint.swift */; }; 6E3D0FDD78ECC20F4B3697ED21D64224 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C2B218B5EAA5831A036BBF7E1EFC1B9 /* MediaPlayer.framework */; }; - 6EA446E553C33A1F676CE6B3C7ACE147 /* RDEPUBChapterRuntimeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2335B9D3BB17D5D8C1AE0AF4D9365BD4 /* RDEPUBChapterRuntimeStore.swift */; }; - 6F3BBAE11AD5528F26A3EAB35A3D8ECF /* RDEPUBChapterPageCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB1D59EE26106C87AE53E9F9ECC96BB /* RDEPUBChapterPageCounter.swift */; }; + 6E71A9CA46078CFBC110C52B11389A4B /* DTHTMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FB2B1B0498DCBE7B79BDF49E963E3D /* DTHTMLParser.m */; }; + 6EAC401461BC8CFFC1A85021F3EAAB00 /* RDEPUBTextPaginationSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 923C96CA59176956C63098AF00B0201B /* RDEPUBTextPaginationSupport.swift */; }; + 6F774A3E91C081DC41C567F7673AB0C2 /* SSAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F410ED25E4E84C3D5166261CDF4B58F2 /* SSAlertView.swift */; }; 6FA0125F16DDEDAD341DB75DEB084F4B /* DTHTMLWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = E16D20FA492E085BD0871B107B3077B7 /* DTHTMLWriter.m */; }; 7027EF8D77D4082C1BB9B80FF61FCADC /* DTCoreTextLayoutFrame+Cursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 28043173CA3B3787DC401ACEB5398108 /* DTCoreTextLayoutFrame+Cursor.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 70561AD84616F0AEC6D3FE835A6F74A7 /* SSAlertViewExtention.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2280F2302F54E90122747821FE681543 /* SSAlertViewExtention.swift */; }; - 7075B2D4BF3C6A47BB9906A508D769F5 /* ConstraintLayoutGuide+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A9A82CB094636AFB53D6D130D6219BA /* ConstraintLayoutGuide+Extensions.swift */; }; - 70B30A5F4FE9ABD17B4367269A653E8E /* Archive+ReadingDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934F9BB2B3A313294F121484C3A97A14 /* Archive+ReadingDeprecated.swift */; }; + 71D5FDB9307694CB527C67D15D30157D /* RDEPUBRuntimeChapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CC24C31135ADAF3F1271D5BA167262 /* RDEPUBRuntimeChapter.swift */; }; + 7234AF3FEE44D88C2FA4B78EB30391F1 /* NSMutableArray+DTMoving.m in Sources */ = {isa = PBXBuildFile; fileRef = 53499F40E1790A6CBE8AFEB3AC5BEFC8 /* NSMutableArray+DTMoving.m */; }; + 72E798049EE743AFB3351B8DD2DA36FE /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 53D473C6F07982D3D25781A343EC5A99 /* PrivacyInfo.xcprivacy */; }; 730ACB99580C5982DC08D50FC5084D41 /* DTAttributedTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = A276DE925253460239BFAEC7663F60D3 /* DTAttributedTextView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 7419CE4BFAFC651C051B867282FE2297 /* RDEPUBWebDecorationOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81EAF121C946325DF0FA84458CDFA926 /* RDEPUBWebDecorationOverlayView.swift */; }; + 734D7A5703C1993676CE5460595F276F /* RDReaderView-RDReaderViewAssets in Resources */ = {isa = PBXBuildFile; fileRef = 83410CC9CF2ABE63B90A92F2F988BF65 /* RDReaderView-RDReaderViewAssets */; }; + 742187DFE94AAB5DF65DCCDFFB9A2519 /* RDEPUBTextPositionConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10B86E35282C70276F9B3304A6C550F3 /* RDEPUBTextPositionConverter.swift */; }; 7459A1656EDC29D685073B83561B783E /* NSNumber+RomanNumerals.h in Headers */ = {isa = PBXBuildFile; fileRef = 8552BE1A1F6A6D2879763DE3B2B8A2DD /* NSNumber+RomanNumerals.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 753FB374E263E515914EEA8A41D41B7F /* RDEPUBTextPositionConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10B86E35282C70276F9B3304A6C550F3 /* RDEPUBTextPositionConverter.swift */; }; - 75489D9A756E9D0FE1BCEF136DB94072 /* RDEPUBPaginator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4740BDFF4EED78481148A3109207228A /* RDEPUBPaginator.swift */; }; - 75E391C6630E7493AF23660F84A2A2F4 /* ConstraintLayoutGuideDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4684B406B325B35F39322D75B59B6479 /* ConstraintLayoutGuideDSL.swift */; }; - 761E1E1BBA67C002E2A6C74A8CECE66C /* Archive+Reading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0619F98DA80922E78A07321D0DC54F62 /* Archive+Reading.swift */; }; - 769A0500BBDD01FAFD03C168C295D835 /* NSURL+DTAppLinks.h in Headers */ = {isa = PBXBuildFile; fileRef = 557DA029E15EC99631B647AE8922007F /* NSURL+DTAppLinks.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 77AF3653D38EE679BD8A00B497FAE32C /* ConstraintMultiplierTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BE22EE711B7A38A4443E1C1C12BD79 /* ConstraintMultiplierTarget.swift */; }; + 7492D552569049B3C77F482254C81141 /* RDReaderPageChildViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC645BB4D42BB9762F362DF728F58946 /* RDReaderPageChildViewController.swift */; }; + 763BC0C9739190951DED594259EBCBD1 /* RDEPUBReaderController+ContentDelegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3562A5E853CB707C1AC4E74C3A8E3924 /* RDEPUBReaderController+ContentDelegates.swift */; }; + 76F2DA473044593E8D856BBF41323120 /* ConstraintMakerRelatable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFE19508E12BF8B0B52E8CB4F724217 /* ConstraintMakerRelatable+Extensions.swift */; }; 7821105EF320619993B3BF7FB90C7ADA /* DTCSSStylesheet.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B610E558C4917C4257DC198EAFEE2F6 /* DTCSSStylesheet.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 78A25C8BD48D75A9395327659E683D36 /* DTPieProgressIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DA1E8494B86FFD79C639DCB8C21E1BB /* DTPieProgressIndicator.m */; }; 78D24D82D98D77E744FF73C9040618A8 /* DTHTMLParserNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 11B604FC3B0D5C91FEA763DCC0BE4DC5 /* DTHTMLParserNode.m */; }; - 79321BE2258B95B63DB7427BE14A1AE9 /* RDEPUBReaderController+RuntimeBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F278454696A046C7789CF994A17CE53 /* RDEPUBReaderController+RuntimeBridge.swift */; }; - 79E2065263D1E448793F1CCFE2293ECE /* ConstraintConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7176B677A350927FC670368313DC2CA1 /* ConstraintConfig.swift */; }; - 7A082386EEF40DC6056A27F530FF7B43 /* RDEPUBWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE68175CDCB5EB0A5693C5DD9DD07BFA /* RDEPUBWebView.swift */; }; + 79156E23F0DE0D8314A7ED2C74EF5181 /* RDEPUBAssetRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CCFA3D24C3438DC554760F08D6E8281 /* RDEPUBAssetRepository.swift */; }; + 7934ABD31F35415F9A2C8276676A6648 /* RDEPUBTextBookBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F79D3BC6D895DC606E798E7F15CF3B7 /* RDEPUBTextBookBuilder.swift */; }; 7A48CDFB40202EFD09A2A9AB0EE0770D /* DTCoreTextFontDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 56489D9BA834497E48438A4B4E1CCCFF /* DTCoreTextFontDescriptor.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7A8499BDE42C8ABC4739C5BAEDA0C3A7 /* RDEPUBWebDecorationOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFBCC74BCB4330EB8EF1B57BADBB014B /* RDEPUBWebDecorationOverlayView.swift */; }; 7C05F56B62B15EFF344C5CDE4A52B8FB /* DTHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D4C9B7ED5E04C216AB28E3A4065C1C5 /* DTHTMLElement.m */; }; + 7C0F477270950490612CFDFD2463B894 /* RDEPUBTextAnnotationOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBF22F617BFAC4143D2661C887ACACDB /* RDEPUBTextAnnotationOverlay.swift */; }; + 7C2330640C0E4A460D045292C6CBC7A3 /* RDEPUBReaderSearchBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE6AED7CB30C4F5DF029315D9602ABE2 /* RDEPUBReaderSearchBarView.swift */; }; 7D29C0A53AB1B4C1B257D46EBBDC9DA3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; }; - 7DBDA591B91EDB5C187DDE335235E5A6 /* ConstraintView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DB34ACB132E465CDF8191F7DB71EF6 /* ConstraintView+Extensions.swift */; }; - 7E75DCB27C929A478CAC307A7729EE29 /* RDEPUBReaderTableOfContentsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3636FB4CE567BE823BB3A4326A466986 /* RDEPUBReaderTableOfContentsItem.swift */; }; + 7D692EFA1ED7DF2CE3E867BCE6926A28 /* RDEPUBResourceURLSchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF667FF45F147B7458680F3808B36A4 /* RDEPUBResourceURLSchemeHandler.swift */; }; + 7E829421D4AD915E8A629AEE18B1DB39 /* RDEPUBReaderAssemblyCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA3ACBCB65E911E12245EDA437DF3088 /* RDEPUBReaderAssemblyCoordinator.swift */; }; 7F9CC306B17D409DB903DF850FEDA64C /* DTListItemHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = F7B0087DE7C394A677F5883541AB4FB7 /* DTListItemHTMLElement.m */; }; - 7FBE67FC4DE9151B3E9529926FBE55BA /* NSString+DTURLEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 31BD7533867336DEA9B835F471767178 /* NSString+DTURLEncoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 807CA77A74C46495868ECECF394C0908 /* RDEPUBReaderLocationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D0DCF841D23F8526D19767D66A932DC /* RDEPUBReaderLocationCoordinator.swift */; }; 8099F1E613A15275EF5FC59ED3C73579 /* NSAttributedString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D3D7C8444F58D9B3FCB4F3006D2CFBA /* NSAttributedString+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 80FDABF695B4F0B44F772FE30E4A224C /* RDEPUBReaderSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 591427AC1FAE583EB7EAC76B1E673CDA /* RDEPUBReaderSettings.swift */; }; - 8105CDF3B8B8A7B69EEF5C92718C00E6 /* NSString+DTFormatNumbers.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D86C7063D1128EAEB3A30EBEF214B3F /* NSString+DTFormatNumbers.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 80BF3AEB1634D591238DA13EFCEB9100 /* RDEPUBReaderLocationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D02D5C9FBB27E031A6D934A07FB001F /* RDEPUBReaderLocationCoordinator.swift */; }; + 81114197684AE047BED887B94262BCEC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 1E89FE5F2996336E862E82C1E14E0EE7 /* PrivacyInfo.xcprivacy */; }; 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 */; }; - 814EF5142E9C6BBE23F1F4AD50A12345 /* RDEPUBReaderHighlightsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41AF383869619DE580A94B69EAC6ECF5 /* RDEPUBReaderHighlightsViewController.swift */; }; 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, ); }; }; + 821407D633CEB7380F5CDDADF41FCA04 /* ConstraintMakerRelatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD768C27E392C934C06DFB4AC401037C /* ConstraintMakerRelatable.swift */; }; 83A4BC9FE4765C9B848DD4925D99F362 /* DTAccessibilityElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 99299A9FD6EF739D18352AC5B9325CB0 /* DTAccessibilityElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 845A6C31B9A7E4E57C81CB12A6768494 /* DTHTMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D75FEDE65722CF41F7F32A164376926 /* DTHTMLParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8622AC205E7BC1315C2610BFE74A788F /* RDEPUBWebContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CAFA445DFFA6C8B61C7306D968C7595 /* RDEPUBWebContentView.swift */; }; - 881B7BD0998D85E16EFD2844EAD4770B /* RDReaderSpreadResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7148B56DAB2824CB3E9E617789FD1908 /* RDReaderSpreadResolver.swift */; }; - 88259EAF08B1418D155966286E26DF34 /* DTCustomColoredAccessory.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C6BE3B2DA9A8C4CD3760CDAD6F305A5 /* DTCustomColoredAccessory.m */; }; - 887C1C25E7B69AB387761C1BAE23EEFB /* DTCoreGraphicsUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = A8DFDCAB69B471EB5667C964865C26CF /* DTCoreGraphicsUtils.m */; }; - 89175BE2B58B4C89708B0E58C844A7AB /* Archive+Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA251BAC8B430B3A4D4C4321046B990 /* Archive+Deprecated.swift */; }; - 893152C549C2C7FBD1D4B0AD192A995E /* DTCompatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = B5FCE04EBB553439D1C2BA1001936293 /* DTCompatibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 845CDFFCE815068C10FD4AF0B4E15CA5 /* RDEPUBFontNormalizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AB3159B171D3889AC3B55CCDE7A58F3 /* RDEPUBFontNormalizer.swift */; }; + 855F8BD571BD8EB0F5E7756AC2413C40 /* Archive+MemoryFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174FC0CA05D20BB995E211DE5A0857DB /* Archive+MemoryFile.swift */; }; + 860423ACC47AD21DE09C56F055DB5F28 /* ConstraintInsetTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A5ABB8F3656A110B955B0306E9A8C3 /* ConstraintInsetTarget.swift */; }; + 86A1119C520665097B6028AFE68FB9F6 /* DTSmartPagingScrollView.h in Headers */ = {isa = PBXBuildFile; fileRef = D2BA6D5DBD4C6A9285C4592721790A26 /* DTSmartPagingScrollView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8706619D1EE7AD70C069BFA14EAAC295 /* RDEPUBTextRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59AB14F9464EEFD112E2C56DF09B002D /* RDEPUBTextRenderer.swift */; }; + 871BED7D64905AA927C5EC750B564494 /* NSMutableArray+DTMoving.h in Headers */ = {isa = PBXBuildFile; fileRef = B0AC307F12521444C973EA0DE38B4608 /* NSMutableArray+DTMoving.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8731ED44361D023F68447864DF243AD8 /* RDEPUBParser+Archive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1774BB2BD665DE754F86F839A89C4FBB /* RDEPUBParser+Archive.swift */; }; + 87C52B306599974254236AC5016E4AAD /* Archive+Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA251BAC8B430B3A4D4C4321046B990 /* Archive+Deprecated.swift */; }; + 888BAA8B73F1BCC006F04CED9B65A90F /* RDEPUBReaderBottomToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61CAA3B6C9BC1F1E4C35A108D76B1E79 /* RDEPUBReaderBottomToolView.swift */; }; + 88A1889B200CA2B4581B8F51C55857CF /* cssInjector.js in Resources */ = {isa = PBXBuildFile; fileRef = A9F67CE7DA6170FA2E8E04297DF435E0 /* cssInjector.js */; }; + 88FD881543DD17A4D489DA24B1D82159 /* RDEPUBChapterSummaryDiskCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C33E5B1677A732781A827A0A58805F /* RDEPUBChapterSummaryDiskCache.swift */; }; + 8983E7159E23700FEE6FCACD5E2305CB /* ConstraintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A02EA0374C693F881AB36202FB276DCD /* ConstraintView.swift */; }; 8A0A0FB23DB0021ADC9AD0D90631883A /* NSMutableString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6293E97E128E98559FB74D9A342EAD /* NSMutableString+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8A1237A80B1E82607F28B0B7FEC62AA7 /* RDReaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7CBC59E42E8E03559CFEE4F14A56AC /* RDReaderView.swift */; }; 8A35CC67F0AA6B26397C10D756EA34E8 /* DTTextHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = FB2ABB1D297442D91AA42F1407DB4A58 /* DTTextHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8A8C466E9265295CFB7A9BC165D2F6C4 /* Entry+Serialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F991240340FA026C71B68EB25B9F9C8 /* Entry+Serialization.swift */; }; 8AC90941E71199090BCED76DE89EFB60 /* DTAttributedTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 77A613841EB90827AF321DD50B21DB5D /* DTAttributedTextView.m */; }; 8B405CE13E7D718A9340A82B52766AB2 /* NSCharacterSet+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 75FA8E843D9225BD65EB9D17825EC0F4 /* NSCharacterSet+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8B7DBE63942CFE22AA7A29AF8C18BC83 /* ConstraintInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C241AD9CEC9D6732CFBA6A55B49E64C9 /* ConstraintInsets.swift */; }; - 8B95E586217609BACEFF85D7C5849C42 /* RDEPUBChapterLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9EF15D29CE2E35CD8D3439E3C91EEC9 /* RDEPUBChapterLocation.swift */; }; - 8C051D63625D0279E70383D7939259F8 /* RDEPUBTextLayouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F5BD95D13D1554B8DDB8924913442B /* RDEPUBTextLayouter.swift */; }; - 8D2564C0FE31A0FC8FB2287A295C91C7 /* WeReadApi.js in Resources */ = {isa = PBXBuildFile; fileRef = 017344CEAEFEB304A8BBF76EDF12D835 /* WeReadApi.js */; }; - 8DF6E80E2F1AD0902D7270BCC0C74298 /* NSString+DTURLEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = CB7F3535507F56824BA21509302CF8B3 /* NSString+DTURLEncoding.m */; }; + 8C1D391880A42513665477B22E50398E /* RDReaderTapRegionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552D02CBD7720F274142F052959EB983 /* RDReaderTapRegionHandler.swift */; }; + 8D7778083E1F47B55C1F8D00620B3D68 /* RDEPUBChapterTailNormalizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC956678AB4F5B41D2BA3F5F2A2378A7 /* RDEPUBChapterTailNormalizer.swift */; }; + 8DE4A90F3D321F46BC078221EED31311 /* RDEPUBChapterOffsetMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE57AC7D620527A77DEF6EB1162C9DFC /* RDEPUBChapterOffsetMap.swift */; }; + 8E3F03542AF48E8F627484F74E43F273 /* RDEPUBNavigatorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EC834DF4FD9ABC75E1FDED3BBCB37B0 /* RDEPUBNavigatorState.swift */; }; 8E8726381CBCFB1EDB350183456D4EF7 /* DTHTMLParserTextNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13AB6A32A293E3DC21A7047C6DB958CA /* DTHTMLParserTextNode.m */; }; - 8EBC8430CBF44F64B4FACADBD0B8EF6B /* RDReaderContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ADC9061B1BCC6E2FF6FADF845992FA6 /* RDReaderContentCell.swift */; }; - 8F6A2AAAB01E57DF77A12326E5C90DE6 /* ConstraintDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02CF362F88AB69253F1898E53D7A286A /* ConstraintDSL.swift */; }; - 913E1174EACC09B744C3FFEEFF542B1E /* RDEPUBParser+TOC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2353E8412F8D0EF503DEA9A6178F5948 /* RDEPUBParser+TOC.swift */; }; + 8F2D529DFB7E0B921B2232D93DD11D6F /* rangy-serializer.js in Resources */ = {isa = PBXBuildFile; fileRef = 14423BDEA85698D4A942B9C5F4BA2E17 /* rangy-serializer.js */; }; + 8F7D7DA246BD92907ECFF3E15567C653 /* RDEPUBPageResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCB085E95929F7729CE5CBC9F17BB712 /* RDEPUBPageResolver.swift */; }; + 91740868C9193AB08E7FA6E1330B8DAA /* RDEPUBViewportTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9899192BA37086232FFEA93F5B2EC3B3 /* RDEPUBViewportTypes.swift */; }; 91FDA47810CE5CE2C48AED9205AE89B5 /* DTStylesheetHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = DA25968B7C9A40031DA45BB328EA8A2E /* DTStylesheetHTMLElement.m */; }; - 920E76B340B80738B02FD2C582C1FE46 /* cssInjector.js in Resources */ = {isa = PBXBuildFile; fileRef = A9F67CE7DA6170FA2E8E04297DF435E0 /* cssInjector.js */; }; - 92966B2B3DBC8A298D31A0F2EC7453B7 /* NSURL+DTUnshorten.h in Headers */ = {isa = PBXBuildFile; fileRef = 054674EB90C625BB4BEA9368ED92592B /* NSURL+DTUnshorten.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 92BA0540B424C459F31036AF32CADA5E /* Debugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D495E2AB1223BD4D4909F8FF1FF47DAF /* Debugging.swift */; }; 92C59F96D443525E8833676479FA60C5 /* DTVideoTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 73A99927E52B24583A6A2CF7E6F23848 /* DTVideoTextAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 92C6605AA2E946069E33F146C95E92CB /* DTBlockFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = A0BD48DAF5AE9DB087F2552E99069709 /* DTBlockFunctions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 93538FC1DF1B0A675282F8C0433A1266 /* NSData+DTCrypto.m in Sources */ = {isa = PBXBuildFile; fileRef = 17F750F38183302BF345F40F5A4CDD43 /* NSData+DTCrypto.m */; }; - 9463C65355C0D7F791DDFEBD7665DDF8 /* RDEPUBAnnotationModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39816EFB8DFB4FA6B91D9C8AB03A1C6A /* RDEPUBAnnotationModels.swift */; }; + 93332352A5035E9337BEAF6373EA737F /* RDEPUBChapterDataCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3068218D3A08F227DDA6216F94FB69F4 /* RDEPUBChapterDataCache.swift */; }; + 93406FECB9741694A5C3664D2482F7D5 /* ConstraintInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C241AD9CEC9D6732CFBA6A55B49E64C9 /* ConstraintInsets.swift */; }; + 9361F0CAA37A3D0F59158DA361B3403B /* NSArray+DTError.h in Headers */ = {isa = PBXBuildFile; fileRef = CEA93EDA6ED54D374265AA5D6B9EBBA9 /* NSArray+DTError.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9446AB65083BF63454F0BC79597D7A70 /* RDEPUBReaderTopToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9255AF737FE4AA3AE50EE4F2BDCC9AD7 /* RDEPUBReaderTopToolView.swift */; }; 94F5846979DBC90E2B646E975F5ECBA8 /* DTHTMLWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B96B0998B49D8E149C995474DA5FE5F /* DTHTMLWriter.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 950FB487525188C210A2F377151C00BA /* UILayoutSupport+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C647A2EB2720BCBF88B160D124B57BF /* UILayoutSupport+Extensions.swift */; }; - 952F5C1C0DD1FE08A2A9B95457457814 /* ConstraintMakerPrioritizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E138EC76D0DF24544D29D1A4D7429E3 /* ConstraintMakerPrioritizable.swift */; }; + 95F7823641FEAB9FDCC40C27199DAAAB /* RDEPUBTextPageDecorationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 600151C0D6D719C385CC4D3FCA6E0B27 /* RDEPUBTextPageDecorationView.swift */; }; + 961088C5DEA62E762924A9E4158B7931 /* NSURL+DTAppLinks.h in Headers */ = {isa = PBXBuildFile; fileRef = 557DA029E15EC99631B647AE8922007F /* NSURL+DTAppLinks.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 96207BE20EB66606E46D23F61D098367 /* NSURL+DTComparing.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BFCAC7C2AEA867E67C63A852576774F /* NSURL+DTComparing.m */; }; 96507B8910C8C9DE0B221CFF0E8518D0 /* Pods-ReadViewDemo-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = EBA0272559B0F7FAD8AC670AC7431188 /* Pods-ReadViewDemo-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9673AF7E9761F76F91633267FE6955EC /* RDEPUBTextBookModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B80E892056051C2B587A8ACD383A59A /* RDEPUBTextBookModels.swift */; }; + 9705BC91D9FE89A08A6794D92FC852E6 /* NSData+DTCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 02EDFE0EEA47398F3E1A361A73AF6A6C /* NSData+DTCrypto.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 970824C814CB8C9FBEC8916A75542AF3 /* NSURL+DTUnshorten.m in Sources */ = {isa = PBXBuildFile; fileRef = C80065DBA5634DAA71129404975D7349 /* NSURL+DTUnshorten.m */; }; + 9753446AFAFB2D5206BC3815BC726AC7 /* DTActivityTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EEFAE80193E1F0DA53400C9AD641410 /* DTActivityTitleView.m */; }; 9854DC3F1763B6272C2EA3FC6EC40B95 /* NSAttributedStringRunDelegates.h in Headers */ = {isa = PBXBuildFile; fileRef = E20272295EC253826AEE1F83A15D47C5 /* NSAttributedStringRunDelegates.h */; settings = {ATTRIBUTES = (Public, ); }; }; 987538B07E11741899878EA8BC947F51 /* NSCoder+DTCompatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 354C5DD0ACC742762200A640ADA859C6 /* NSCoder+DTCompatibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 98817BF0F2B027A249DB89AE80C178E8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; }; - 988B59EBD2A33A2FD9A02193C13C8CAB /* RDEPUBFragmentMarkerInjector.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4CF544288387FDB24F07552DDE550 /* RDEPUBFragmentMarkerInjector.swift */; }; - 988E47B526BE07E81833417799E2833B /* ConstraintLayoutSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50297AAB8278F2BB6FCFE82B20C7A049 /* ConstraintLayoutSupport.swift */; }; - 98EFAF63F225123A24C13391F5881BBB /* FileManager+ZIPDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F9B298989305B9569B71E448E8C1D4 /* FileManager+ZIPDeprecated.swift */; }; + 98A96688CD654BD1ECB83A4A6C35B2B5 /* RDEPUBJavaScriptBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB11AFA7344329F42A0DB0609F061C2B /* RDEPUBJavaScriptBridge.swift */; }; + 98F0C9511AA2FA40CD80DC71EB6D4FDF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; }; 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, ); }; }; - 999B9B214FCBD51D40BDA3C1310D3867 /* SSAlertPresentAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E481007A8174943868EBC81CF3B04B9F /* SSAlertPresentAnimation.swift */; }; - 99C4B9A6CC62724B7BFBF8ADB17C618E /* RDReaderTapRegionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552D02CBD7720F274142F052959EB983 /* RDReaderTapRegionHandler.swift */; }; + 99B5F1A7842B8564F8082C29DD139E50 /* RDEPUBReaderTableOfContentsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 902D8268D209ABBB2768E10905B189C2 /* RDEPUBReaderTableOfContentsItem.swift */; }; + 99EED2CEABDAD89BA879A10A967AF73C /* Archive+ZIP64.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5693BD171B879406EA14C97F4AD8D8 /* Archive+ZIP64.swift */; }; + 9AE3623ACABFA3CF97A614D014EAFBD7 /* ConstraintDirectionalInsetTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = A651CEE8062C84A597DBC44ECA3DBCC1 /* ConstraintDirectionalInsetTarget.swift */; }; + 9AF5A960FC572176D3B411E21E5C15E3 /* RDEPUBWebView+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9AB5C9C4F800F35807FC8C8C016D37 /* RDEPUBWebView+Search.swift */; }; 9B199D71D2F48B2055A7FDF9BD7051CC /* NSString+CSS.m in Sources */ = {isa = PBXBuildFile; fileRef = E27C503C8128BC3CE109FFE077AEF325 /* NSString+CSS.m */; }; - 9BCA2AC7CB0DE92A10C51B78F496D67B /* RDEPUBSearchModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5B4E39C9AFC826DEBB56E697B1F4FBE /* RDEPUBSearchModels.swift */; }; + 9B5B3177B11F60D56864D6B23599A1A4 /* RDEPUBWebView+Reflowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBD671BD778F560BB9C93218055756E1 /* RDEPUBWebView+Reflowable.swift */; }; + 9BA35FC883C9B3C63EE3D0BFEC53689A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0834E84632886CDD9911CAB76183F1CE /* QuartzCore.framework */; }; + 9BC6941C2AFB6676D1D700EA2F6C96F3 /* RDEPUBWebContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E5D40E2A0541080719F92638244887 /* RDEPUBWebContentView.swift */; }; 9BD146F72B29C703798001F138B069C2 /* NSCoder+DTCompatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 9129D37C7F89AD8FD530663BB4503FC0 /* NSCoder+DTCompatibility.m */; }; - 9C2186EA4B21504D6507CE501BABFBF4 /* NSArray+DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = 73940D215D644B6815A09DA802CFBDDF /* NSArray+DTError.m */; }; - 9CB1F58C6CCE41A375FEEFE9C1C59795 /* NSURL+DTAppLinks.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D36F7CDCD7D5788CDCB3A1421DB242B /* NSURL+DTAppLinks.m */; }; - 9D0F7B1A685E8FDE76647F9B3B2F64BF /* wxread-default.css in Resources */ = {isa = PBXBuildFile; fileRef = 752EEC76100E31C66D33F087A318A0B8 /* wxread-default.css */; }; - 9D3DD0AC19334C572E7F2B4E6AFE0E6C /* ConstraintMakerRelatable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFE19508E12BF8B0B52E8CB4F724217 /* ConstraintMakerRelatable+Extensions.swift */; }; - 9D551B01D2565BB99465813B43D9A73B /* RDEPUBPageBreakPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EFAAE86BA4FF308A74FA1CEAD40CB4 /* RDEPUBPageBreakPolicy.swift */; }; - 9D78CCA3439AC9188A5C183A9A00D473 /* RDEPUBCoreTextPageFrameFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9B5CCE470E634B18B32B423369C1A31 /* RDEPUBCoreTextPageFrameFactory.swift */; }; - 9DD8B22C4E99C2A8303A6EEAE802E5CC /* RDReaderViewProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3653FA7AD3EF76156E4FB425AC90E5F4 /* RDReaderViewProtocols.swift */; }; + 9C0255A17E35943423E7D7AB969AC7F6 /* RDEPUBTextSearchEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8967AE46A1F72D8F08E5B81E1FE83ADA /* RDEPUBTextSearchEngine.swift */; }; + 9C0E5A978B5A27AC8C09ED68B9764422 /* NSURL+DTAppLinks.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D36F7CDCD7D5788CDCB3A1421DB242B /* NSURL+DTAppLinks.m */; }; + 9C3540F5054EF5CC5D8B122C39934B17 /* DTFoundation-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 5FAB3C7F19A511C2B1B7DFBA3CA308C4 /* DTFoundation-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C3E94E23AE8C0AD6111BCF14AEC76D8 /* RDEPUBWebView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EF09515D5DAA9464888E02EA64AA5F7 /* RDEPUBWebView+Configuration.swift */; }; + 9C672E99DC82792E146EB7630466CD20 /* SnapKit-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CE078B465FDEF40EC0469626A2C2469 /* SnapKit-dummy.m */; }; + 9C78E8CD41D34DF80AC196077012CFD7 /* ConstraintLayoutGuideDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4684B406B325B35F39322D75B59B6479 /* ConstraintLayoutGuideDSL.swift */; }; + 9CEF7F2CF595080103EEE04A25BED20D /* NSData+DTCrypto.m in Sources */ = {isa = PBXBuildFile; fileRef = 17F750F38183302BF345F40F5A4CDD43 /* NSData+DTCrypto.m */; }; 9DE04C83A68C7512C0A860FFF5CEC113 /* DTCoreText.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F98F0F060B066CAFBCD3C85C26B2B4 /* DTCoreText.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9E446579CEB5E8BC164603FE7CE15101 /* RDEPUBReadingLocationModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CD5FB30A34088E10667AF4D2A8A6C3 /* RDEPUBReadingLocationModels.swift */; }; 9E4AB71DE8B3E7218B53B2C8EF6B843A /* DTAttributedTextContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = F0D72C5680EBE6CA716EE77281AA4436 /* DTAttributedTextContentView.m */; }; - 9E5EE2F88FD00579AB3DDBF7AD8A0AE8 /* RDEPUBTextPaginationInterfaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B791CDD0D79122F60EC136C025BC751 /* RDEPUBTextPaginationInterfaces.swift */; }; - 9FFC1191E3FE7A42DB62B217A165374A /* RDEPUBReaderController+RenderSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0281BCEC7C2071D2996CBAC72E78DC2 /* RDEPUBReaderController+RenderSupport.swift */; }; - A053E75E8D26143FA6E7CCE25034D3DA /* Archive+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2659DB0685E16ABAE0382F726DA8C8 /* Archive+Helpers.swift */; }; + 9EA52F173814DF82FFAC811892158AF7 /* RDEPUBReaderConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7CF983E6CF4797FA35E3B583ECA941 /* RDEPUBReaderConfiguration.swift */; }; + 9EB7F99A75B6651DCCEC63A716AA543B /* RDEPUBReaderTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B1E75CAB3F7C45A794F6C5335FFECBB /* RDEPUBReaderTheme.swift */; }; + 9F9C7582F35DDD1AFB5AB289D5BCA7D5 /* RDEPUBRuntimePageCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21EFC0B1DC22A993EB8B62715E87FC0 /* RDEPUBRuntimePageCount.swift */; }; + A01919793CF5504D2A234FCFEF12B961 /* RDEPUBTypesettingPipeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0B9221B1E7C6BE99BF31D7EA5C9AA94 /* RDEPUBTypesettingPipeline.swift */; }; + A023C6257ACFCB4BB2C3A0A780D3BA69 /* DTCompatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = B5FCE04EBB553439D1C2BA1001936293 /* DTCompatibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A0CA0D794B98DDD54311F0784A7214C5 /* DTCustomColoredAccessory.h in Headers */ = {isa = PBXBuildFile; fileRef = 33DB57DF8C5AE334B1BBE9F989352F1D /* DTCustomColoredAccessory.h */; settings = {ATTRIBUTES = (Public, ); }; }; A132A0C9EB13B65FB886B11AA2E6304C /* NSDictionary+DTCoreText.h in Headers */ = {isa = PBXBuildFile; fileRef = 27205D159B39090908CA0E9C3997B784 /* NSDictionary+DTCoreText.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A1A80C07F342BF3A07CA033606A6A552 /* RDEPUBAttachmentNormalizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091717314CB1F573F5EE5EEC4DF86BA9 /* RDEPUBAttachmentNormalizer.swift */; }; - A1BF991FC54C5FD49E55A52E6A0F2F7C /* RDEPUBRenderDiagnosticsCollector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F7C000E05ED51ADF331A7F6244782A5 /* RDEPUBRenderDiagnosticsCollector.swift */; }; + A13DAFC3F7B4C05DCC4AF5A3FE472CC9 /* RDEPUBChapterPageCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB1D59EE26106C87AE53E9F9ECC96BB /* RDEPUBChapterPageCounter.swift */; }; + A1EE5A08AF16702B7DF8D3881B1DA9C1 /* DTHTMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D75FEDE65722CF41F7F32A164376926 /* DTHTMLParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A295FBBCF3BEA4000036E0415ED6C9EB /* ConstraintRelatableTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885653999BD237A574B67DB279DE7E87 /* ConstraintRelatableTarget.swift */; }; A2CA9C27EB2B77637A12723F8CFE7AAB /* NSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 4428633C8EC3F6289CCF376F588CE0C4 /* NSString+HTML.m */; }; + A2E8647998FBD87C93B2C64C4C9DD6C1 /* epub-bridge.js in Resources */ = {isa = PBXBuildFile; fileRef = 834C4DDB8B590618F7AA98A3FD2B9D2E /* epub-bridge.js */; }; A30D5C1E413A3D319A83D3410A8A549F /* DTCoreTextParagraphStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F760D62F6C6F083374702146337F2CB /* DTCoreTextParagraphStyle.m */; }; - A3A69DDFE22FBC1F582BC14B6126830C /* SnapKit-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CE078B465FDEF40EC0469626A2C2469 /* SnapKit-dummy.m */; }; - A50B62E0566BA314560E1819BAB75818 /* RDEPUBResourceResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E4EDBF01B37189D38063CF62178D33 /* RDEPUBResourceResolver.swift */; }; - A5633B025E0AA8C922E35775EA0AC5E0 /* SSAlertDefaultAnmation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81B8F42440B802E6EC2B1FF7E3022F3A /* SSAlertDefaultAnmation.swift */; }; + A337239791E84EF6E8715CBCE689637C /* RDEPUBStyleSheetBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C42316556BF2EEC09C00E6296E6B2D /* RDEPUBStyleSheetBuilder.swift */; }; + A43A5EA6C160F881A43859992FDEC335 /* NSString+DTUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = BE547E464E5D34F9FFB24BAC81D1E4CC /* NSString+DTUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A4E30E61CCDF03F8ABBCA6E261A9158A /* DTSmartPagingScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 96EEB6B022E22A4ABC86D87469D4815B /* DTSmartPagingScrollView.m */; }; + A625C258A5EB2D38A3B9160DD77FDD8E /* NSString+DTPaths.m in Sources */ = {isa = PBXBuildFile; fileRef = A3565FEE19F19CB43A43409AFFF2BD2C /* NSString+DTPaths.m */; }; + A6A0B209CBE2DB0BF38825BB49D9BD40 /* RDEPUBParser+Resources.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1A6EBFEF43855FDA02B545BAAC10BB8 /* RDEPUBParser+Resources.swift */; }; + A6E152416030ADA9833117FB49E6C6A5 /* ConstraintMakerFinalizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B32EA4B7A492EEF26CE260E9AEFA0B /* ConstraintMakerFinalizable.swift */; }; A72E9F983E5BBFA7485EFB482B80E89E /* DTWeakSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 5155CCDD61E030184249A020FE5D74B0 /* DTWeakSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; A7D665BBDADBE3D64023FF52E218FCFA /* DTCoreTextConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = B2B3D0CB47B0A8E9EC5AF48B7F2DD6B0 /* DTCoreTextConstants.m */; }; - A7F8C570D541E1F1EC1A8E4A2EC2E68C /* RDEPUBTextLayoutFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD2D83120D70130690765D762EE062E /* RDEPUBTextLayoutFrame.swift */; }; - A833E24935D4F228FD3BD9BEAB740069 /* ZIPFoundation-ZIPFoundation_Privacy in Resources */ = {isa = PBXBuildFile; fileRef = CA4A114775B98CEA4A566C18123B145F /* ZIPFoundation-ZIPFoundation_Privacy */; }; - A8E83D12A6E410D0FC8A5DBE43C52590 /* RDEPUBParser+ReadingProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FE003BF0CD443CC90388E203EEF3FC8 /* RDEPUBParser+ReadingProfile.swift */; }; - A9826B61AF63CFD1C4CCF19C9FD08662 /* DTFoundation-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = FCC718115E5C31C88A680522AE36B089 /* DTFoundation-dummy.m */; }; - A9D844A2F27BA98D3BA75873EDD65C59 /* RDEPUBPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99158A091EAB840DE3F822364077954D /* RDEPUBPreferences.swift */; }; - AA303B108A49B03A4DE75B195BEFBE75 /* DTPieProgressIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B443D035BA5652B03A41BE905C5AC17 /* DTPieProgressIndicator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A8766359A57A5265AA4477967BC2D372 /* RDEPUBChapterWindowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B02BB9570D018CF824C0024AEDA9DF92 /* RDEPUBChapterWindowCoordinator.swift */; }; + A8D8E9F0F2486DA5E8B6BB91285E23ED /* RDEPUBTextRendererSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B3FB8E803FAC9694F24527C90EFBB6 /* RDEPUBTextRendererSupport.swift */; }; + A91D815127AC86ACCE9A29979442C534 /* default.css in Resources */ = {isa = PBXBuildFile; fileRef = 05D57D16346FAF830F1F82067307EA9F /* default.css */; }; + A950B0683A4FD6CE58A10B2357AD603D /* ConstraintPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45C7129757B54DFD9E445D0A17D73A83 /* ConstraintPriority.swift */; }; AA3722CC8B4992EDA8A9F5E9116835B7 /* DTCoreText-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 015F3EE69253C66E11B931E3360BA5FE /* DTCoreText-dummy.m */; }; - AB36563CC3EDC235699CCEB6D1484066 /* NSString+DTFormatNumbers.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ACA5E519F100582F0D772FCD76125C0 /* NSString+DTFormatNumbers.m */; }; - AB684E2A99C8F730CA898A4470395362 /* RDReaderView-RDReaderViewAssets in Resources */ = {isa = PBXBuildFile; fileRef = 83410CC9CF2ABE63B90A92F2F988BF65 /* RDReaderView-RDReaderViewAssets */; }; - AB9C03C2161D535EA9BB736D9EB8B049 /* RDEPUBReaderAssemblyCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6EEF32CBD14BBE7F831F9049D474D53 /* RDEPUBReaderAssemblyCoordinator.swift */; }; - AC349599AF6B7B81AB3D4B4430893BCC /* RDEPUBReaderSearchCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89F87AEC289B931D4CA74AD9BACE1736 /* RDEPUBReaderSearchCoordinator.swift */; }; + AA4167676147564A6AC27274F160C86B /* RDEPUBPageBreakPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EFAAE86BA4FF308A74FA1CEAD40CB4 /* RDEPUBPageBreakPolicy.swift */; }; + AAC840547DD17D5C75BE4893AEC920D1 /* SnapKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B10D3EC1701BBA4D69926915F942558 /* SnapKit-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AAF33DBF3EDE2E462850017949EB0A59 /* RDEPUBRenderDiagnosticsCollector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F7C000E05ED51ADF331A7F6244782A5 /* RDEPUBRenderDiagnosticsCollector.swift */; }; + ABA0A65005939E1FB82E3B3A4F091C78 /* ConstraintDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02CF362F88AB69253F1898E53D7A286A /* ConstraintDSL.swift */; }; + ABEC4B76C9F6444D84E18C262ACCF327 /* DTFoundationConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = FD03567681A79FFB6E81B3105503065D /* DTFoundationConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC7000465258822B466855B12F1F97A3 /* DTCoreTextGlyphRun.h in Headers */ = {isa = PBXBuildFile; fileRef = 49A21AE1A0AB5383F895B34EA7D6973D /* DTCoreTextGlyphRun.h */; settings = {ATTRIBUTES = (Public, ); }; }; - AD4D993DAAFB5D79FD9FF0730ABF4D0B /* URL+ZIP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537BB06317FA188DF7A8A960152520E8 /* URL+ZIP.swift */; }; - AD539146F04C1A6F229006A5388A8EDD /* DTLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B007AEEACE9EA271DD73AFD80A74D83 /* DTLog.m */; }; - ADC53AFF68D3D9CBBCBCFC5D4DD97303 /* RDEPUBNavigatorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EC834DF4FD9ABC75E1FDED3BBCB37B0 /* RDEPUBNavigatorState.swift */; }; - AE54C8FD8A185D0635D6977951DF5AEE /* String+SHA256.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F6D9DC5EAEE10F7D8644F06CEE3C99 /* String+SHA256.swift */; }; - AF6200E5AABBF295D6FA3E41957E322C /* ConstraintConstantTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F090A0289338B14050FAF16C22605B0 /* ConstraintConstantTarget.swift */; }; - AF751E58599AA3DDF6E0E044A31C9E61 /* RDEPUBPublication.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8EED36C051D89B37BBEB069A27F2099 /* RDEPUBPublication.swift */; }; + ACB17BD54175BC3B98AC1175481E6AED /* RDEPUBSearchModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5B4E39C9AFC826DEBB56E697B1F4FBE /* RDEPUBSearchModels.swift */; }; + AD17641501D68E8C21FAEC49743E4106 /* ZIPFoundation-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = D927F128EBF26AD2C61FD0E6B6F6F5ED /* ZIPFoundation-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ADA3C68B75F77F86802CE889179486CD /* SSAlertSwift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DE4FA5133891593CB45AF36B8EBF60C /* SSAlertSwift-dummy.m */; }; + AED36CEE168D0B0F38E9278A6072D11D /* Data+Serialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC78D0B0F37BF9AD1058CACA9AAF9BE /* Data+Serialization.swift */; }; + AEF232AD2E90CAD431398B4472B54BC5 /* RDEPUBModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 407122F95D1113F8C0E241B944F75AFE /* RDEPUBModels.swift */; }; + AEFE45633FE7519934D64924E411B57E /* DTTiledLayerWithoutFade.h in Headers */ = {isa = PBXBuildFile; fileRef = DF5EDCA3B21964D0462BE0982E2F0BF8 /* DTTiledLayerWithoutFade.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AF1180FE77173DEED2AD3291BC152E1A /* RDEPUBChapterRuntimeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73C1CEF117C3E1E15A1C0F0CE6E34DC /* RDEPUBChapterRuntimeStore.swift */; }; + AF39816C18685EDACE0933D4A2FFDDCA /* RDEPUBReaderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A3D6CCECEDECBA9575590F2349E274 /* RDEPUBReaderDelegate.swift */; }; AF7A1DC953992A149CB0815C90769529 /* DTIframeTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B931AB189BDDE2CCE8578B50BD60AF0 /* DTIframeTextAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B098641C2F013BC6531D283D239D9B61 /* ConstraintMaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60657D08CCDB38BFED30BD40F4720CE4 /* ConstraintMaker.swift */; }; - B10FE27CC58D55D2C18F4F71A2BE43D0 /* FileManager+ZIP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27B7D3C6F0C845E3D42BBA613FFB0465 /* FileManager+ZIP.swift */; }; - B12F0E87FEAB61906615AA24A89C7868 /* RDEPUBTextPageRenderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7516CFAD05F8C77E5C44B21BAA4E0FD2 /* RDEPUBTextPageRenderView.swift */; }; + B01557AA9F0C45612AE1C7ADB1CCE4FC /* FileManager+ZIP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27B7D3C6F0C845E3D42BBA613FFB0465 /* FileManager+ZIP.swift */; }; + B2418E155320F3D2B933ACC133B1E946 /* NSDictionary+DTError.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC368BD2A8D266EB771E5FB9A5A5B3C /* NSDictionary+DTError.h */; settings = {ATTRIBUTES = (Public, ); }; }; B24FAD34A7915FC4B90FE9038E814FF2 /* DTLazyImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = A4E43DCDE0C8751557E7E65FEEB1C865 /* DTLazyImageView.m */; }; - B2A8DFD01894EA8B42B8E42E3057799A /* DTFolderMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = B245F7E143F3091652C64F6593E33F7A /* DTFolderMonitor.m */; }; - B4D3AE83F584470109BF585432C6AF7D /* RDEPUBTextPageDecorationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAB6B4692FB6CD7D4AF9C67B872DBA82 /* RDEPUBTextPageDecorationView.swift */; }; - B50EA676BE4541E31653213E59CCAB38 /* RDEPUBTextBookBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F79D3BC6D895DC606E798E7F15CF3B7 /* RDEPUBTextBookBuilder.swift */; }; - B57E7C8E379A4A4EF754FC7072D33A39 /* ConstraintMakerFinalizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B32EA4B7A492EEF26CE260E9AEFA0B /* ConstraintMakerFinalizable.swift */; }; - B749C708327563022F4ABF12CF3A9E94 /* RDEPUBWebView+FixedLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22BDEB6AC81168F4F3FE78ACCEC2A32D /* RDEPUBWebView+FixedLayout.swift */; }; + B4A84796AE5BF4ED6A1D644618A332D6 /* RDEPUBBuildDiagnosticsReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7B78125ED71DFD5D7AB87570EE951A /* RDEPUBBuildDiagnosticsReporter.swift */; }; + B505DF10D47ED5D2EE65BAE5B5BF335A /* RDEPUBDTCoreTextRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2858C4311A2E3D5C75501279AD0FFE5 /* RDEPUBDTCoreTextRenderer.swift */; }; + B510C7A442F6FE2E206976EFFAC000FD /* RDEPUBTextBookModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B80E892056051C2B587A8ACD383A59A /* RDEPUBTextBookModels.swift */; }; + B653D862AF128CEEDFD71129AE620A7D /* ConstraintLayoutGuide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6BA0ECCB0ACF8580C3B5192CA9F11B /* ConstraintLayoutGuide.swift */; }; + B6934B2BD7ABDF8AC0858D96B5AF08C5 /* wxread-replace-latin.css in Resources */ = {isa = PBXBuildFile; fileRef = 314D77A91FF74A0ACD762C92A6C1AF19 /* wxread-replace-latin.css */; }; + B6FF3D1C24E6AF28D71A0CD7F5F18189 /* RDReaderView-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F521195CC116D5FFE8AFDDA24ABA2CD /* RDReaderView-dummy.m */; }; + B75282EB6F6EA0AD668AECD3C83F66BD /* DTVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BD9F2023FF826F67E69E3407A70A8A6 /* DTVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; B7BDD79FC06A1E4E0E17592860844108 /* NSAttributedString+DTCoreText.h in Headers */ = {isa = PBXBuildFile; fileRef = FF3F7BA20A1213804048557D946CEEA9 /* NSAttributedString+DTCoreText.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B7CEA88BC794B1C614273D425F1E5048 /* RDEPUBPageLayoutSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7E7F66A26C2FC81CA8C11585A91C6C /* RDEPUBPageLayoutSnapshot.swift */; }; B8332F36582F7A001FE85A8D54B27C9E /* DTCoreTextParagraphStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D62C5531667F364F14DB67D33FFA554 /* DTCoreTextParagraphStyle.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B8420EA8242E549CC4A2068633959D02 /* DTSmartPagingScrollView.h in Headers */ = {isa = PBXBuildFile; fileRef = D2BA6D5DBD4C6A9285C4592721790A26 /* DTSmartPagingScrollView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B8517869241495A627A33561CB97D369 /* RDEPUBReaderConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B81846EA13E575E1459B41F96ACB244 /* RDEPUBReaderConfiguration.swift */; }; B86493B996A888D1DA7814ECB6028E22 /* DTHorizontalRuleHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = A68D72764140BC3DF67A2B9E52CEC7A4 /* DTHorizontalRuleHTMLElement.m */; }; + B8B8E917005BED05B7FFDB7A6F7FB2B9 /* RDEPUBReaderController+PublicAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26C542F1825BDD5743D52350B8F4788D /* RDEPUBReaderController+PublicAPI.swift */; }; + B91AF32D50A82B44DDAFD1D77382AB2D /* DTExtendedFileAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 538572A5C098252D629F2185F1B1C9E8 /* DTExtendedFileAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; }; B94CD41727DFCA8791EED8FB2CA397DD /* DTCompatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 2702E05AA7A266B7D8D0F881FD4E09DA /* DTCompatibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BA3D4212AC53469F96C5F4A5770491B2 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0834E84632886CDD9911CAB76183F1CE /* QuartzCore.framework */; }; - BABB04BF50AC580EC52084CA431CCF71 /* ConstraintDirectionalInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91144FC099DD458E61625CD939577DAA /* ConstraintDirectionalInsets.swift */; }; - BB08A213FC1D81987E2B1094CCA018E0 /* RDEPUBPageCountCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B0AA875D62D53E865F10DC10DB4A4E /* RDEPUBPageCountCache.swift */; }; - BC211BF30DBBDAC998C506BEAFABD491 /* RDEPUBReaderAnnotationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F66B88725D7C653DDBD5551A375FD47B /* RDEPUBReaderAnnotationCoordinator.swift */; }; - BC66D1AB07E873288D6D0FCEB06B29FE /* DTLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A1EBB9497F4846C84CA0BF5E4F84FC7 /* DTLog.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BD4A60A13E46761D3FBDA962A5237091 /* RDEPUBReaderTopToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D9882EB39FEC92F33CDC352AF14F3E /* RDEPUBReaderTopToolView.swift */; }; - BD59F2A94A227D316D6BE58D23439651 /* epub-fixed-layout.html in Resources */ = {isa = PBXBuildFile; fileRef = 5FA33EEE837A94425518AA187AAEF524 /* epub-fixed-layout.html */; }; - BE123FB6EA9D908EFAC4B1F8C2DA44E5 /* RDEPUBHTMLNormalizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C337B19D3E4AFBDAEB7B261AA881E5C /* RDEPUBHTMLNormalizer.swift */; }; + B9F653EC455154C78DE01B878E886926 /* RDEPUBWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE68175CDCB5EB0A5693C5DD9DD07BFA /* RDEPUBWebView.swift */; }; + BBA44069DE29E393B2A3CE8DD7DF6552 /* DTAnimatedGIF.h in Headers */ = {isa = PBXBuildFile; fileRef = A9FBC69C7DA29A7283920FDA9EBBCE8A /* DTAnimatedGIF.h */; settings = {ATTRIBUTES = (Public, ); }; }; BE12CD112F82AD9050D7FB29395F27F9 /* DTVideoTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = 63A6EDF10DA5A051C6CDCBE5D97AF11E /* DTVideoTextAttachment.m */; }; - BF779F9500C6DF3A569494C8830C96B0 /* RDEPUBViewportTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E58B496CE64A4A768BD528773C58791A /* RDEPUBViewportTypes.swift */; }; - BFF12CDB6761C929D95D99E0BD1AC32D /* RDEPUBFontNormalizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AB3159B171D3889AC3B55CCDE7A58F3 /* RDEPUBFontNormalizer.swift */; }; - C4EB180D0B3B630F5A7A2B1583B5E794 /* RDEPUBReadingSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26A9B74B4E5F952B9DC79C10B5D8E7F0 /* RDEPUBReadingSession.swift */; }; + C126E3F113976F437DCE56A43F333F01 /* epub-fixed-layout.html in Resources */ = {isa = PBXBuildFile; fileRef = 5FA33EEE837A94425518AA187AAEF524 /* epub-fixed-layout.html */; }; + C20DE912EC839E6C24E6986FD8BB54BF /* RDEPUBReaderController+RenderSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAE8DA169B8772C7191FBA9AB80092F8 /* RDEPUBReaderController+RenderSupport.swift */; }; + C35E6F015F735469B32D4B64F92B06EF /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD081272B47A3B73CE693D21DC26827 /* Entry.swift */; }; + C3700FAB589DC03A0A08C41B115C52AF /* ConstraintRelation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3593ADF561D422F73C8EABAD4A4F440 /* ConstraintRelation.swift */; }; + C38BA41ED0310D484DE9BC50683409A1 /* RDEPUBSelectionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF725A63AD2B9E43F33EB99F07C304D4 /* RDEPUBSelectionState.swift */; }; C5EE8E7373FBD24E66C678356393D871 /* DTCoreTextFontCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E791886E59DC0FAB02C6BBAB5BE5B6B /* DTCoreTextFontCollection.m */; }; - C6AF83C094C274A61C7CB4062A3943DC /* ConstraintPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45C7129757B54DFD9E445D0A17D73A83 /* ConstraintPriority.swift */; }; + C681A4D2502FBF00BF6CE3D0947B032B /* RDEPUBReaderSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AD6EF544C3C1F037842DF5E4CD048B0 /* RDEPUBReaderSettingsViewController.swift */; }; C6B65C0EA759DF426FBBBB113BA3C3E8 /* DTBreakHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = F734214E067BDBDB581911E1A5CDAADB /* DTBreakHTMLElement.m */; }; + C6D061E40B11A03C582F08F0FE658A7B /* RDReaderContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ADC9061B1BCC6E2FF6FADF845992FA6 /* RDReaderContentCell.swift */; }; C72CE4B974DFAE2C3037D425EB559314 /* DTCoreTextLayoutFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = CE6907AC04870132CCE3177E894607DD /* DTCoreTextLayoutFrame.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C739C4D9352ACAF9B7203686B775E540 /* wxread-replace-latin.css in Resources */ = {isa = PBXBuildFile; fileRef = 314D77A91FF74A0ACD762C92A6C1AF19 /* wxread-replace-latin.css */; }; - C7FFD3B0C1516632BD8D3E4868C61E79 /* RDEPUBWebView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EF09515D5DAA9464888E02EA64AA5F7 /* RDEPUBWebView+Configuration.swift */; }; + C80A0D159BF6BC3D103A63B0302293B9 /* RDEPUBTextContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC30482E1E56725E225659BE42ED79EC /* RDEPUBTextContentView.swift */; }; + C83D41DE82AB76091B97114C28172D19 /* RDEPUBPageInteractionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012E2E783F3E83E70D3A5CE2DD8DE007 /* RDEPUBPageInteractionController.swift */; }; C8F753B56A6C4D2A938B6EA68CF522F8 /* DTColorFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 86A0361390AA0C29BC1A5487083A12A7 /* DTColorFunctions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C99F2011C8DC83BC6FC00055B08011D2 /* NSString+DTPaths.m in Sources */ = {isa = PBXBuildFile; fileRef = A3565FEE19F19CB43A43409AFFF2BD2C /* NSString+DTPaths.m */; }; + C94D3CDEE5621CEE53D409852A4882BD /* Entry+ZIP64.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345CE45F696634CEA692A86D16F684C /* Entry+ZIP64.swift */; }; + C96A2A5DFFCFA753C0A7558F62717D2D /* ConstraintAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9593E7B37569219F8D9A504B47707DC /* ConstraintAttributes.swift */; }; C9DD05EF2D27FFEBFA7BF6F437168501 /* DTCoreTextConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = C8C20D9A71E6605DEE80342E7122B683 /* DTCoreTextConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; - CA832E0649A6753992FEFCD55E1B778B /* NSDictionary+DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = C18480913E002315E3F5EC6945371697 /* NSDictionary+DTError.m */; }; + CA67E186452744CFE975CDC8CF75640F /* RDEPUBTextBuildPipelineInterfaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31318B9A80CC694F9EFE2A72D4826B64 /* RDEPUBTextBuildPipelineInterfaces.swift */; }; + CA7AD3BB11AECE506ADDF8C25F279904 /* RDEPUBRenderRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44F8832CEF100545E84932BE0B82CA7B /* RDEPUBRenderRequest.swift */; }; + CC3A3AC5FDD073E23A4FB9751E1906F6 /* DTLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A1EBB9497F4846C84CA0BF5E4F84FC7 /* DTLog.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CC63E1E8EA43990861286233CB21034A /* RDEPUBTextIndexTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAF8513093523204F2598DC45941FB49 /* RDEPUBTextIndexTable.swift */; }; CC8B8F3D758E98B51F69EDFAA69663A4 /* NSString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 8ABDEFEA94DC6408D92AC66DA3625DE5 /* NSString+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; }; - CD0A44B836906158422FD41F5F86A0A3 /* DTBlockFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 375179DD3A4576CEF947E1760BBDC69B /* DTBlockFunctions.m */; }; - CED780B0DC05E2B70B44A38853D5E0CD /* rangy-core.js in Resources */ = {isa = PBXBuildFile; fileRef = DA5D38D76DC2B327F6FA86DD38AD5079 /* rangy-core.js */; }; + CDC18A86DDAEC766C5C19B8C5E9C95B1 /* RDEPUBReaderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20E5F9A5250EB5724D516BF67152CBB8 /* RDEPUBReaderController.swift */; }; + CFA89FA29A6C84CD39425D4D1C97F8C2 /* DTLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B007AEEACE9EA271DD73AFD80A74D83 /* DTLog.m */; }; CFC49467FE75BFABC22A1CE2FFA26570 /* DTImage+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 12A5EA26EE7539EDDA90649D22FB675D /* DTImage+HTML.m */; }; - CFF1C5073FEB03082CCD3F8CBC662F6E /* RDEPUBTextBookCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B3FF18F25CAE9935ED05E0856F20C3 /* RDEPUBTextBookCache.swift */; }; - D0EC11725D1CA8C1CDDB18BC6BD93ADF /* ZIPFoundation-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = D927F128EBF26AD2C61FD0E6B6F6F5ED /* ZIPFoundation-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D0B50C95B7602E93C6813B56F9909F3A /* RDEPUBReaderToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16A1F97BF3EA1335405B877D02898D34 /* RDEPUBReaderToolView.swift */; }; D1A93B2D7F1B312755C48790338D7712 /* DTIframeTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = AA3CFD777AD76F97EBA05B3920F5786A /* DTIframeTextAttachment.m */; }; - D1AE34A627F20F309CC48C8989352200 /* Data+Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5964AF1336E2F0DA0E2C32991E76C78C /* Data+Compression.swift */; }; - D35237860CD654AAE9D67BBDC710A4B8 /* RDReaderView+CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 579711B0A35AF68319DBD269278F32FC /* RDReaderView+CollectionView.swift */; }; - D356836F1638F813B0F4C5F8AAA150C6 /* RDEPUBPaginationCacheCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2883B11ACE6B06556D1B0FC8DC0F7E30 /* RDEPUBPaginationCacheCoordinator.swift */; }; - D424AA977964AE888E41EAC1D169DD1C /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 53D473C6F07982D3D25781A343EC5A99 /* PrivacyInfo.xcprivacy */; }; - D42D898AD123808E26CFA93BDA62F8AC /* RDURLReaderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4FF37961C00CE5162EE9165F6EC5E1A /* RDURLReaderController.swift */; }; + D39500A78C93625973C27E090B69D956 /* Typealiases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 945D4549E0B9168D7DDB910DE874B874 /* Typealiases.swift */; }; + D3F314D751A10537FC13519B351B2E04 /* RDEPUBReaderChromeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC629A6C0155AB2BFFCA818A0C89567A /* RDEPUBReaderChromeCoordinator.swift */; }; D526E03C098DA6CF8F01C1BC126C9BBA /* DTCoreText-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7768E097F8BDE9FBE3752B98CD0101 /* DTCoreText-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D5A0CAEBA0B0F55C2C0F885EE8B3B69E /* RDReaderFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1DF1B2EEF04D6470AB3196636E73075 /* RDReaderFlowLayout.swift */; }; - D699A38F72CF8A51F0A3E5A6BF91259D /* NSString+DTUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = BE547E464E5D34F9FFB24BAC81D1E4CC /* NSString+DTUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D6A349786DE991EE40FA199ABEF53AE2 /* RDEPUBStyleSheetComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3DD09EADFE152D5050A931432C2C60 /* RDEPUBStyleSheetComposer.swift */; }; - D6BFB6720734FB0EC3D287CB03A28AD4 /* RDEPUBLocationConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F07C5EDFE63EB17570FE17E8E6E1B8 /* RDEPUBLocationConverter.swift */; }; - D72E29B6BDF79056DB0196324B801659 /* RDEPUBSelectionOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF55EE5FC41651D54FEAD1F8097F034 /* RDEPUBSelectionOverlayView.swift */; }; + D67FD0F33281FD59AEDE6A35DEDCCA80 /* DTVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF6156128A1A0CA24D9D06E346B9445 /* DTVersion.m */; }; + D712A797298A0126776AD6F485C7C224 /* RDEPUBCoreTextPageFrameFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9B5CCE470E634B18B32B423369C1A31 /* RDEPUBCoreTextPageFrameFactory.swift */; }; + D738CA9F50C3937A49E911CD58A59262 /* RDEPUBParser+Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D4CD395C5569628D36C4D838AC6A8B8 /* RDEPUBParser+Package.swift */; }; D82DDA4C1E5C4D5378A9E5EA3F8E9DFE /* DTBreakHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 533CFFF04BE7BC4BDEAE11DF675A84CA /* DTBreakHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D9619B2333335421D6BAEE499C684389 /* Archive+Progress.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD987C2FA7C8FBC96FD024E421DA0E68 /* Archive+Progress.swift */; }; + DA4000DFBE1DD93EA927F26438A55DEE /* ConstraintDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6578DFAB51436E8DF266FD087700E26B /* ConstraintDescription.swift */; }; DA5B51E5D80C161D2902B6FC60DA5EC8 /* DTImageTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 426D442C01742E4361F5BC34D43152A6 /* DTImageTextAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; DAD16D3E6EBB32FF96C63BFE6037065F /* DTLinkButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1B75050E71BDD96FC19EC6BA129E6A /* DTLinkButton.m */; }; - DB867D24B763CC5A0FCC4B37E4CF822B /* RDEPUBChapterTailNormalizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC956678AB4F5B41D2BA3F5F2A2378A7 /* RDEPUBChapterTailNormalizer.swift */; }; - DB96436F5A79D5AD419C48D9283608E9 /* DTExtendedFileAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 538572A5C098252D629F2185F1B1C9E8 /* DTExtendedFileAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DAD3AB7B9072C8C4A948BBFF1EBE45B0 /* UIImage+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = D85A85D891A320165843EF9F489178F7 /* UIImage+DTFoundation.m */; }; + DBC2DD8B83F5404F00CC1481D2FA70ED /* SSAlertCommonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE4EE750340347ED584C5A2C1164331A /* SSAlertCommonView.swift */; }; DBCA42F7E1697F4EC82E0E7C9657851D /* DTAttributedTextCell.h in Headers */ = {isa = PBXBuildFile; fileRef = D4093D30B372960168C1349100DF7DE0 /* DTAttributedTextCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; - DBD5BC69F5BF488734E93EB1591A7455 /* ConstraintLayoutSupportDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5B34EE2160C27947D1EEFD42184CF5D /* ConstraintLayoutSupportDSL.swift */; }; - DBF505F4ACCE320791D8152854E9354D /* UIImage+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = D85A85D891A320165843EF9F489178F7 /* UIImage+DTFoundation.m */; }; + DC16500BB2DC867D159E2B4795FD2E00 /* Archive+BackingConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61E36C4FE448DDDA03450847FCB785B6 /* Archive+BackingConfiguration.swift */; }; DC3E2932A82AD5D88D8B1EFBF004BEC8 /* DTCSSListStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = B509CD4B498635D4536245E43D802ED9 /* DTCSSListStyle.m */; }; + DC9D87AC6D207651A205478E4595A360 /* Date+ZIP.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76A97683FB5E8E8B86EA4AD71F6F4B4 /* Date+ZIP.swift */; }; DD73CD0E5148961D1ABD80E050122314 /* DTCoreTextFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = A1B031E2B903C2F177195C1C3D1D1EDB /* DTCoreTextFunctions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DE46AF71DEEF00410A44069968B60E32 /* SnapKit-SnapKit_Privacy in Resources */ = {isa = PBXBuildFile; fileRef = B9DCB5EC0B1CDADD221717CADDF62359 /* SnapKit-SnapKit_Privacy */; }; DE68AD2F426FD462BC70F968E5AF5553 /* DTTextAttachmentHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ABC2F552388CD08EC1EFDEF606AE222 /* DTTextAttachmentHTMLElement.m */; }; - DF8FDCB60CF766E247D02C515719A4A7 /* NSString+DTUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DCECDBD73595CA88A2208000B4B68352 /* NSString+DTUtilities.m */; }; + DE6F45CCE420BC18B98C151A8E83BC6D /* RDEPUBReaderSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0C8E542EFB0872BF4471EB2EDB06EA /* RDEPUBReaderSettings.swift */; }; + DF0CD02EC4C094F4454701FBC1D7B1AF /* RDEPUBParser+ReadingProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FE003BF0CD443CC90388E203EEF3FC8 /* RDEPUBParser+ReadingProfile.swift */; }; + DF611CA46A11935B195229A13F367FE3 /* DTCoreGraphicsUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = B6425B2235BA00FE865AF58EF44F1144 /* DTCoreGraphicsUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DF811FAAD2A380EF8C303151A273183A /* Data+Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5964AF1336E2F0DA0E2C32991E76C78C /* Data+Compression.swift */; }; DFB5A1C64C7908FD05BEFF7C7B571353 /* DTAttributedLabel.h in Headers */ = {isa = PBXBuildFile; fileRef = 5679D281C282896E6FC352C48E0AF439 /* DTAttributedLabel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E048208FDCFDEAEF6DBB9B40DB89D77A /* NSString+DTUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DCECDBD73595CA88A2208000B4B68352 /* NSString+DTUtilities.m */; }; + E052221C319BA638FAA2C82325694B42 /* DTCoreGraphicsUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = A8DFDCAB69B471EB5667C964865C26CF /* DTCoreGraphicsUtils.m */; }; + E0FE3B76A6ABF85CFED6D5E8E98E7CD4 /* NSString+DTURLEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = CB7F3535507F56824BA21509302CF8B3 /* NSString+DTURLEncoding.m */; }; + E10E5848ED673CF04502FEED81F1B0D7 /* RDEPUBReaderSearchCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2451995F95C8B333F972A39BDFC7EBCE /* RDEPUBReaderSearchCoordinator.swift */; }; + E1100C64BC2685999DCD2A291413C52B /* RDEPUBReaderDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C0665F2995A9C45FCAABC3F62DE3D /* RDEPUBReaderDependencies.swift */; }; + E1635E1C51B4232C2868F80F501E2CCB /* DTActivityTitleView.h in Headers */ = {isa = PBXBuildFile; fileRef = 8272D0B59ABD6652D0CAE3DFCC2C5EE1 /* DTActivityTitleView.h */; settings = {ATTRIBUTES = (Public, ); }; }; E1DA58105A72D6C0B77A8A9F7822BD68 /* DTCoreTextLayoutFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 029130886CE8E422D16ADF97468CB1B0 /* DTCoreTextLayoutFrame.m */; }; - E26725CBBF02CE92FE5DB944A79F6732 /* RDEPUBTextSelectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D96987335475BD8BA2521AFC376AA33C /* RDEPUBTextSelectionController.swift */; }; + E215C744B7C0F0F1468F0B1BA37A5A26 /* NSString+DTFormatNumbers.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D86C7063D1128EAEB3A30EBEF214B3F /* NSString+DTFormatNumbers.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E2D6AF839C0B606034F40459920DA24A /* Debugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D495E2AB1223BD4D4909F8FF1FF47DAF /* Debugging.swift */; }; + E3044B1AAB959E23ECFADBAC78250AF2 /* RDEPUBPublication.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8EED36C051D89B37BBEB069A27F2099 /* RDEPUBPublication.swift */; }; + E352C433119E17F8C66192B46BF51C46 /* RDEPUBPageLayoutSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 357CF3DD6846FD95FE05679E6ED3866F /* RDEPUBPageLayoutSnapshot.swift */; }; E38FC7829B5BDD1588FA487E0FBAC208 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; }; - E62C939CE97E5B1E751569F1FAAB082A /* RDEPUBReaderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC57BC47A53EE7FA53B0C3CD69FA66D /* RDEPUBReaderDelegate.swift */; }; - E651CDB1967286FAB7D7B68291FF6A86 /* DTBase64Coding.m in Sources */ = {isa = PBXBuildFile; fileRef = 061BD3911E4E530C792E65CED7868C33 /* DTBase64Coding.m */; }; - E67F588B1E229877C200B96784118C44 /* DTVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF6156128A1A0CA24D9D06E346B9445 /* DTVersion.m */; }; - E7155FB098C605592505A8C976DF7901 /* epub-bridge.js in Resources */ = {isa = PBXBuildFile; fileRef = 834C4DDB8B590618F7AA98A3FD2B9D2E /* epub-bridge.js */; }; - E77F49061B5055BDBBB16114D7D4E882 /* DTExtendedFileAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = F5389D4E96A4AFE175691B4D3D4524E7 /* DTExtendedFileAttributes.m */; }; - E8A9845F331539C2091F5DDD08C0A848 /* RDEPUBChapterDataCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC748DACF16D50DAE82F5524E3BC41AB /* RDEPUBChapterDataCache.swift */; }; + E3EB2F12234336BBC7BD786BF4B15EA8 /* SSAlertDefaultAnmation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81B8F42440B802E6EC2B1FF7E3022F3A /* SSAlertDefaultAnmation.swift */; }; + E50DB2DE7D674E6FBFB6D4AF5255A84F /* RDEPUBTextPageRenderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F2782574376AE28B6377B05472691E6 /* RDEPUBTextPageRenderView.swift */; }; + E630FEF41AB35A857F55864A56BDE8E8 /* ConstraintOffsetTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F950CEF31AC0065C0BEAB26BA2CEFA1 /* ConstraintOffsetTarget.swift */; }; + E6BC4DC52C8602301A04EEBC41A7C54D /* RDReaderPreloadController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC152E542DB1CE18D5FB239C50E7DD27 /* RDReaderPreloadController.swift */; }; + E80CE3276D704909789FE7AFC3AD71C3 /* UIScreen+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = A3765B289CB7E6C5DF6160D8EBC83C3C /* UIScreen+DTFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E8452D0B6E2745B0A8BA84317F01D75D /* RDEPUBChapterLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BBD50992F22841D962895AED7442C7 /* RDEPUBChapterLocation.swift */; }; + E88BE97E9C863275B42FAE9A893B011E /* RDReaderView-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = A09C96468ACCEB23603142794D15D1FA /* RDReaderView-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E8A8019AA3033E666C0E67B6C6C4DAF3 /* RDEPUBTextLayouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F5BD95D13D1554B8DDB8924913442B /* RDEPUBTextLayouter.swift */; }; E8AB5CE26A5224EF09E9E363D70C7775 /* DTDictationPlaceholderView.m in Sources */ = {isa = PBXBuildFile; fileRef = AFE0B56638AFB77F4455784A7ED59876 /* DTDictationPlaceholderView.m */; }; - E95293260204B17F0A630D8E6F6CAA84 /* UIView+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 79A77584AB3B92B2508AE0BCFCB8FD87 /* UIView+DTFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; EA0040823CCF8E97363DB5891F5DA6D0 /* DTCoreTextFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F2D44B82815CA3397BE2C9F64819B88 /* DTCoreTextFunctions.m */; }; + EA7C6CE570DA224EE0181273422A6B14 /* wxread-replace.css in Resources */ = {isa = PBXBuildFile; fileRef = 4C61D04931F7736E429371D872F7E736 /* wxread-replace.css */; }; + EB1183B3D404746EBC0FB8949E816BA4 /* DTPieProgressIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DA1E8494B86FFD79C639DCB8C21E1BB /* DTPieProgressIndicator.m */; }; EBB7E4D4DB253B5F1267CF9B4DDB4E93 /* DTCoreTextFontDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCA590DB72AA93275262A414F16578A /* DTCoreTextFontDescriptor.m */; }; - ED021F861A73CA0AE92DBA03C972F296 /* RDEPUBJavaScriptBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB11AFA7344329F42A0DB0609F061C2B /* RDEPUBJavaScriptBridge.swift */; }; EDA269B595D70EC2B9D81DC890157D63 /* DTAccessibilityViewProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = F37563A56C180CE18EEFADE56FA3C17F /* DTAccessibilityViewProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EDAC7618BC92FC4B459F158E06BDD6C7 /* RDEPUBChapterWindowSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE46828BF035DBBC8F92EAF87053BB89 /* RDEPUBChapterWindowSnapshot.swift */; }; EE4F0992A0F597320FF7BDE4691D0FD4 /* NSString+CSS.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D6C48939A8498A09DCFC7F41ECB8A25 /* NSString+CSS.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EF2F5AAF11933BBE5B1CCEAA829954B1 /* RDEPUBNavigatorLayoutContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B36F32273C511FB9B6C51CE36A6DD9 /* RDEPUBNavigatorLayoutContext.swift */; }; - F002E4B5F2AA5C5814904D907E424E04 /* DTCoreGraphicsUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = B6425B2235BA00FE865AF58EF44F1144 /* DTCoreGraphicsUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EE5DD52AC6049A014EB83D27D68F8A0E /* NSFileWrapper+DTCopying.m in Sources */ = {isa = PBXBuildFile; fileRef = 1756C6FFCA695290BA62DA4E7B6DD20B /* NSFileWrapper+DTCopying.m */; }; + EECBE7DEC22B34CA111F62AD9A487313 /* RDEPUBPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99158A091EAB840DE3F822364077954D /* RDEPUBPreferences.swift */; }; F006F73F17F2E3CC0E7852E9357BA946 /* DTAnchorHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 424F12BEB0EDAAF1C719B5103D30C653 /* DTAnchorHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F0614580B5D04710FC659553B52609CC /* RDEPUBChapterOffsetMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = E500EE11964861814042CF5C8E3EA3A3 /* RDEPUBChapterOffsetMap.swift */; }; + F02A297AAA5834EC31C5206E18F15E7A /* RDEPUBReaderChapterListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA26E2111C84E605445082FAD421636 /* RDEPUBReaderChapterListController.swift */; }; F0F631056BADA32253528247DF493272 /* DTHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = EE9D4A455723797A27F04A15C4E47823 /* DTHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F1068326AAA87C25D4C0BD09FF1DB537 /* DTFoundationConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = FD03567681A79FFB6E81B3105503065D /* DTFoundationConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F13680D7C98C3D63DE4E1C48BB0D6629 /* RDEPUBParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CE0D7B6DB4A2FC4CFBD7F402AD7F6FA /* RDEPUBParser.swift */; }; + F14DD745C5ED6624BDAA23CF1C7AA23B /* RDEPUBTextPerformanceSampler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5336F230E9CBA5516A0C4F48CC80B2 /* RDEPUBTextPerformanceSampler.swift */; }; F1E9B6D0728711AE4D7154616DBC17EC /* Pods-ReadViewDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = AA0811310099A4BCB3D52683B70DB3D1 /* Pods-ReadViewDemo-dummy.m */; }; - F2694378DEBE2779E51DE1C817FD21C6 /* RDEPUBReaderDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290EA639B2AE8B8092D7C08A581CD970 /* RDEPUBReaderDependencies.swift */; }; - F2D41D78F78CC4596D14A0484C85B5EF /* RDEPUBChapterData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D1621642B6C2702FFE6606483DEF37 /* RDEPUBChapterData.swift */; }; - F2F9007A3CD34CC638174F8C067583BF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; }; + F3A9470F157B110967AB1544A7BD0DCB /* RDEPUBWebViewDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B4DD7F34461088F19AB93FBE13CC9BD /* RDEPUBWebViewDebug.swift */; }; F4A2E3C4237B8931F61B72FF0BDD6D69 /* DTObjectTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = F038FB491B9A4A53447EF0DC333301AE /* DTObjectTextAttachment.m */; }; - F4CBA7CAF93F2E7CDA1F48103F1E2196 /* SSAlertSwift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 937CEFA81DA4227008DBFB114783A08B /* SSAlertSwift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F4DCBD93181A99F95FDE58AEA73E53F0 /* SnapKit-SnapKit_Privacy in Resources */ = {isa = PBXBuildFile; fileRef = B9DCB5EC0B1CDADD221717CADDF62359 /* SnapKit-SnapKit_Privacy */; }; + F4FB9D77B89F3CE38F95B2B9C8BDC780 /* ZIPFoundation-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A06A36ADBE678DB44158D32E751FAFF /* ZIPFoundation-dummy.m */; }; + F5763770B3C5C71AECB56DCE794CACF6 /* RDEPUBReaderRuntime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94798D2C9B825493840EF99A7447AFB7 /* RDEPUBReaderRuntime.swift */; }; F58ABA106C6200175DF574DF05A75BA8 /* DTAccessibilityElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F0486D3B46DC92AB254D6EE9CC233BF /* DTAccessibilityElement.m */; }; F5A424E67F41C4F7B830CE2F7E71570D /* DTCoreText-Resources in Resources */ = {isa = PBXBuildFile; fileRef = F4E2B1AC73977F10254628C0B7EBE38F /* DTCoreText-Resources */; }; - F5EB0B62950DEEE558D889D1003AF432 /* NSFileWrapper+DTCopying.m in Sources */ = {isa = PBXBuildFile; fileRef = 1756C6FFCA695290BA62DA4E7B6DD20B /* NSFileWrapper+DTCopying.m */; }; F6036BBDFE7484F5A453F6876C97C098 /* NSAttributedString+SmallCaps.m in Sources */ = {isa = PBXBuildFile; fileRef = 76F99C8130EE83A22CD223C7F5E6E66E /* NSAttributedString+SmallCaps.m */; }; F61102C9BF3D6296CB6C6B754ABF98C5 /* DTTextHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CFEE298DD52246B46E6FA62B16B38DF /* DTTextHTMLElement.m */; }; - F6A3CC6F64BE9D2E5AD45D2821196111 /* LayoutConstraintItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8C0D83EB6EDE71B264B6B0DD4644F5 /* LayoutConstraintItem.swift */; }; - F8314CC437C8756EDE5CE69D6011AAB6 /* DTFolderMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 2101691D5743105B2E3E40376584B91C /* DTFolderMonitor.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F8BF98E83C90D04AA3379C9A4623D7E2 /* RDReaderView-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F521195CC116D5FFE8AFDDA24ABA2CD /* RDReaderView-dummy.m */; }; - FB02F142A6ED6908D3AEC074B4CB113D /* RDPlainTextBookBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB57CE3D5558A2995D7CEF1CAD00079 /* RDPlainTextBookBuilder.swift */; }; - FB65E0C6E2E9AB5FCDAFC6090BEF2CFE /* DTVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BD9F2023FF826F67E69E3407A70A8A6 /* DTVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F68A4B34606ED464F14D79CA47432BDF /* wxread-dark.css in Resources */ = {isa = PBXBuildFile; fileRef = C4C7097CA92DB08445EE4D912804A41B /* wxread-dark.css */; }; + F92EA25D82E65C4BE170A66D429F3B5A /* ConstraintLayoutSupportDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5B34EE2160C27947D1EEFD42184CF5D /* ConstraintLayoutSupportDSL.swift */; }; + F9C877C6095F38943FBB625B32FB23A5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; }; + F9DBAFE16D14195BDCFBAAC4FDE3CFD4 /* DTExtendedFileAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = F5389D4E96A4AFE175691B4D3D4524E7 /* DTExtendedFileAttributes.m */; }; + FBE148A72A9BB6C0E85F0AADDF975FB8 /* DTTiledLayerWithoutFade.m in Sources */ = {isa = PBXBuildFile; fileRef = 70F5D03B9B3B7F182C8F5C1F5D4FA7E7 /* DTTiledLayerWithoutFade.m */; }; FC5F52919451A15639442B46B38E4C67 /* DTCoreTextLayoutFrame+Cursor.m in Sources */ = {isa = PBXBuildFile; fileRef = E62C5E593037EF8A45E7FAA847D0277C /* DTCoreTextLayoutFrame+Cursor.m */; }; - FD30AEA440323EDF5AD0C41402570F01 /* NSDictionary+DTError.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC368BD2A8D266EB771E5FB9A5A5B3C /* NSDictionary+DTError.h */; settings = {ATTRIBUTES = (Public, ); }; }; FD5D68887297CFA94F543C49DC68A156 /* DTHTMLAttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 7C496AB6A73C2D0BD0FF340A6A2960D5 /* DTHTMLAttributedStringBuilder.m */; }; - FD81151580A55178103D6CCC596A9574 /* SSAlertSwift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DE4FA5133891593CB45AF36B8EBF60C /* SSAlertSwift-dummy.m */; }; - FDC76C5EFA6E71299BCC5F4BD028FD3D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50104DE2E35D820ECA22CDB3E6F96EA /* Foundation.framework */; }; - FE128B6AE2BC2AF0F05B3A0878D17B06 /* RDEPUBReaderRuntime.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE5CC526B804F5385241DD0D5A71831E /* RDEPUBReaderRuntime.swift */; }; - FE539F115EB066FEC060F71C7772CDF1 /* RDEPUBBookPageMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1E1631C6F8CBDBBDDC1DC595AF83F4E /* RDEPUBBookPageMap.swift */; }; - FE58F512BA753D11477A7116C3D5082D /* RDEPUBTextAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E676DB9AB5DFC0FB4A33EC63DDACC0E8 /* RDEPUBTextAnchor.swift */; }; - FE5D015FCE8DAE214ED92143FCCDDA10 /* RDEPUBPageResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5192CA781CBC54A3FDB730E48E1F25A /* RDEPUBPageResolver.swift */; }; - FE8CA7EF8AA87C97549223C9A02251F7 /* RDEPUBTextPaginationSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 923C96CA59176956C63098AF00B0201B /* RDEPUBTextPaginationSupport.swift */; }; FEB00C614DCB6A8A7BDF8DEE11260984 /* NSMutableAttributedString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = B0500CA69405E23B09291359EB28331E /* NSMutableAttributedString+HTML.m */; }; + FFBA4341534F87364A2EC11AD286397D /* RDEPUBHTMLNormalizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C337B19D3E4AFBDAEB7B261AA881E5C /* RDEPUBHTMLNormalizer.swift */; }; + FFF409F0205F1745B8B3AC89917F07A3 /* DTBlockFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 375179DD3A4576CEF947E1760BBDC69B /* DTBlockFunctions.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 0054D393C28B889558DF0D00390F5769 /* PBXContainerItemProxy */ = { + 1C5F512837ED73C03A0016C6C71019B6 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = AA2E57587AA8EECA63C4BE08EA3CB6D2; - remoteInfo = RDReaderView; + remoteGlobalIDString = 8F6E5A5BF72D62CDFD25F91A7CFA3309; + remoteInfo = DTFoundation; }; - 15BED367A216D80838EB7C12554E0653 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = AA15C8469D67684160CC2A7098EB841C; - remoteInfo = ZIPFoundation; - }; - 2FEB97D4CCD7A71799CA75E1BF5AD6AA /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = B88F4EA0695B6B3165C64594850D72C7; - remoteInfo = DTCoreText; - }; - 3BC6E6A89A1BD3072A8FBC04721656F7 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 8619D5ADECF2B26CFD9A9826D61D289A; - remoteInfo = SSAlertSwift; - }; - 4282060097D663E353F2B35BA95A21B2 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = B88F4EA0695B6B3165C64594850D72C7; - remoteInfo = DTCoreText; - }; - 715CA2D4D1E6CFF1F48AA221E8CC1B40 /* PBXContainerItemProxy */ = { + 2074E58B021F8317A9C99BF7BA77373D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; remoteGlobalIDString = 19622742EBA51E823D6DAE3F8CDBFAD4; remoteInfo = SnapKit; }; - 854B45A7C1AAF89785F301AF0E6D33C7 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 8F6E5A5BF72D62CDFD25F91A7CFA3309; - remoteInfo = DTFoundation; - }; - 9F1F7047ABEB860A15FC928201C5E0E5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = C7A8D82E407CD3FDC3BA55CEE519B252; - remoteInfo = "ZIPFoundation-ZIPFoundation_Privacy"; - }; - A3AFCE91FFD7D4DCCD796B7E018FA543 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 8F6E5A5BF72D62CDFD25F91A7CFA3309; - remoteInfo = DTFoundation; - }; - A67A76B78EB9B8A3BFABD5C713E7BA63 /* PBXContainerItemProxy */ = { + 29937A7B398A9C3E1C58905655B3DB1E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; remoteGlobalIDString = 0C24CB0E87A728A11AA1124CB360D6A1; remoteInfo = "DTCoreText-Resources"; }; - AA3ADC832F3674BDD5726D99641EB9E5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = AE7F393FB7805DE2664AB4111873F907; - remoteInfo = "RDReaderView-RDReaderViewAssets"; - }; - B73BD2CE6BDD286244B88561CE2C3E44 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = AA15C8469D67684160CC2A7098EB841C; - remoteInfo = ZIPFoundation; - }; - C5B70D53764B58C82081EE2758B8790A /* PBXContainerItemProxy */ = { + 2CDE2C5C25B94E29D0E991B83FCC525D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; remoteGlobalIDString = 8619D5ADECF2B26CFD9A9826D61D289A; remoteInfo = SSAlertSwift; }; - E5375B19EF8369B17DD045C5C6AB9590 /* PBXContainerItemProxy */ = { + 5DE91FF74FCD48024A2680A11AA59C68 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = 19622742EBA51E823D6DAE3F8CDBFAD4; - remoteInfo = SnapKit; + remoteGlobalIDString = AA15C8469D67684160CC2A7098EB841C; + remoteInfo = ZIPFoundation; }; - F6BF41ACECDA86F5762F7F276E86BD1B /* PBXContainerItemProxy */ = { + A3F5D978334155622A97BB4625069CA1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8619D5ADECF2B26CFD9A9826D61D289A; + remoteInfo = SSAlertSwift; + }; + A51E17C7DB24793DA4AD107C12EC554D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = AA15C8469D67684160CC2A7098EB841C; + remoteInfo = ZIPFoundation; + }; + A9CA67AE9091496AECCD25CE058E0EB4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B88F4EA0695B6B3165C64594850D72C7; + remoteInfo = DTCoreText; + }; + AAA9B1811E0CF13CD7ED2D08F75A4F29 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; remoteGlobalIDString = 8A8DB685241263AFDF5E6B20FE67B93A; remoteInfo = "SnapKit-SnapKit_Privacy"; }; + AB7EFADB0158CA56FA326C1085D102D9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 19622742EBA51E823D6DAE3F8CDBFAD4; + remoteInfo = SnapKit; + }; + D2195D28B2165E92956DD46A264456AF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = AA2E57587AA8EECA63C4BE08EA3CB6D2; + remoteInfo = RDReaderView; + }; + DD757D1119D7FD9CFB9803BB8F180DF0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = AE7F393FB7805DE2664AB4111873F907; + remoteInfo = "RDReaderView-RDReaderViewAssets"; + }; + DE06E9EC48201B0A1486D4E40137598D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = C7A8D82E407CD3FDC3BA55CEE519B252; + remoteInfo = "ZIPFoundation-ZIPFoundation_Privacy"; + }; + E21CAAA5EA7C676E5E305C3B1B88D982 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8F6E5A5BF72D62CDFD25F91A7CFA3309; + remoteInfo = DTFoundation; + }; + F0FA44D7E63FF42A6C497438761F3B77 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B88F4EA0695B6B3165C64594850D72C7; + remoteInfo = DTCoreText; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 012E2E783F3E83E70D3A5CE2DD8DE007 /* RDEPUBPageInteractionController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPageInteractionController.swift; sourceTree = ""; }; 015F3EE69253C66E11B931E3360BA5FE /* DTCoreText-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "DTCoreText-dummy.m"; sourceTree = ""; }; 017344CEAEFEB304A8BBF76EDF12D835 /* WeReadApi.js */ = {isa = PBXFileReference; includeInIndex = 1; name = WeReadApi.js; path = Sources/RDReaderView/EPUBCore/Resources/WeReadApi.js; 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 = ""; }; 045ADBF2087ADD914A9CA0B62EBF2A9F /* Pods-ReadViewDemo.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-ReadViewDemo.modulemap"; sourceTree = ""; }; - 0463606FFE0A076D81FC1ECDE5C47259 /* RDEPUBChapterWindowCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterWindowCoordinator.swift; 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 = ""; }; 05D57D16346FAF830F1F82067307EA9F /* default.css */ = {isa = PBXFileReference; includeInIndex = 1; name = default.css; path = Core/Source/default.css; sourceTree = ""; }; - 05DFBF4E36FCC77E8BE2B51DF47B7F33 /* RDEPUBRuntimeChapter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBRuntimeChapter.swift; sourceTree = ""; }; 0619F98DA80922E78A07321D0DC54F62 /* Archive+Reading.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Archive+Reading.swift"; path = "Sources/ZIPFoundation/Archive+Reading.swift"; sourceTree = ""; }; 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 = ""; }; + 0813D235A2C7FFD9AC3F254EFC9F5FFC /* RDEPUBReaderController+RuntimeBridge.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBReaderController+RuntimeBridge.swift"; 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; }; 08E72A1A5E0D8524A2374DA24CDD8494 /* RDEPUBWebView+JavaScriptBridge.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBWebView+JavaScriptBridge.swift"; sourceTree = ""; }; 091717314CB1F573F5EE5EEC4DF86BA9 /* RDEPUBAttachmentNormalizer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBAttachmentNormalizer.swift; sourceTree = ""; }; @@ -581,6 +582,7 @@ 0A5E0CB7F87D274C44FEEA77C659EDCC /* ResourceBundle-RDReaderViewAssets-RDReaderView-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-RDReaderViewAssets-RDReaderView-Info.plist"; sourceTree = ""; }; 0AB1D59EE26106C87AE53E9F9ECC96BB /* RDEPUBChapterPageCounter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterPageCounter.swift; sourceTree = ""; }; 0B10D3EC1701BBA4D69926915F942558 /* SnapKit-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SnapKit-umbrella.h"; sourceTree = ""; }; + 0B2963687ADF8375028F8C6F50B1F40B /* RDEPUBPageCountCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPageCountCache.swift; sourceTree = ""; }; 0B6B10413287246F7D530687A388D83A /* RDEPUBPaginationModels.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPaginationModels.swift; sourceTree = ""; }; 0B7771FDE9AB1688166584C0AA03F12A /* DTCoreTextFontCollection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextFontCollection.h; path = Core/Source/DTCoreTextFontCollection.h; sourceTree = ""; }; 0B80E892056051C2B587A8ACD383A59A /* RDEPUBTextBookModels.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextBookModels.swift; sourceTree = ""; }; @@ -589,18 +591,18 @@ 0D7B78125ED71DFD5D7AB87570EE951A /* RDEPUBBuildDiagnosticsReporter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBBuildDiagnosticsReporter.swift; 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 = ""; }; - 0F02953DC627CB32FFD38E101007BB9E /* RDEPUBReaderController+DataSource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBReaderController+DataSource.swift"; sourceTree = ""; }; 0F5D34047BAA894BEF1DFA23E6A9DAB1 /* DTCoreTextMacros.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextMacros.h; path = Core/Source/DTCoreTextMacros.h; sourceTree = ""; }; 0FC368BD2A8D266EB771E5FB9A5A5B3C /* NSDictionary+DTError.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+DTError.h"; path = "Core/Source/NSDictionary+DTError.h"; sourceTree = ""; }; - 10AF8170B881EC5CFA3C1CA4AC3E27F1 /* RDEPUBReaderChromeCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderChromeCoordinator.swift; sourceTree = ""; }; 10B86E35282C70276F9B3304A6C550F3 /* RDEPUBTextPositionConverter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextPositionConverter.swift; sourceTree = ""; }; 11B3FF18F25CAE9935ED05E0856F20C3 /* RDEPUBTextBookCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextBookCache.swift; sourceTree = ""; }; 11B604FC3B0D5C91FEA763DCC0BE4DC5 /* DTHTMLParserNode.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLParserNode.m; path = Core/Source/DTHTMLParserNode.m; sourceTree = ""; }; + 12109749AFDE57CACC5E56AB63AD3EB2 /* RDEPUBReaderContext.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderContext.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 = ""; }; 13AB6A32A293E3DC21A7047C6DB958CA /* DTHTMLParserTextNode.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLParserTextNode.m; path = Core/Source/DTHTMLParserTextNode.m; sourceTree = ""; }; 13E1548FC42118F1A4164739DE6BE4E9 /* UIFont+DTCoreText.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIFont+DTCoreText.m"; path = "Core/Source/UIFont+DTCoreText.m"; sourceTree = ""; }; 14423BDEA85698D4A942B9C5F4BA2E17 /* rangy-serializer.js */ = {isa = PBXFileReference; includeInIndex = 1; name = "rangy-serializer.js"; path = "Sources/RDReaderView/EPUBCore/Resources/rangy-serializer.js"; 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 = ""; }; + 16A1F97BF3EA1335405B877D02898D34 /* RDEPUBReaderToolView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderToolView.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 = ""; }; 1774BB2BD665DE754F86F839A89C4FBB /* RDEPUBParser+Archive.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBParser+Archive.swift"; sourceTree = ""; }; @@ -612,79 +614,77 @@ 1B443D035BA5652B03A41BE905C5AC17 /* DTPieProgressIndicator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTPieProgressIndicator.h; path = Core/Source/iOS/DTPieProgressIndicator.h; sourceTree = ""; }; 1C8F0DCEAA5A3DF38C503CA30DC94C1C /* Archive.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Archive.swift; path = Sources/ZIPFoundation/Archive.swift; sourceTree = ""; }; 1CD2D83120D70130690765D762EE062E /* RDEPUBTextLayoutFrame.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextLayoutFrame.swift; sourceTree = ""; }; - 1D0DCF841D23F8526D19767D66A932DC /* RDEPUBReaderLocationCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderLocationCoordinator.swift; sourceTree = ""; }; - 1D44639116A69F6C16EC09917C13DD18 /* RDEPUBReaderController+TableOfContents.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBReaderController+TableOfContents.swift"; sourceTree = ""; }; 1D4C9B7ED5E04C216AB28E3A4065C1C5 /* DTHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLElement.m; path = Core/Source/DTHTMLElement.m; sourceTree = ""; }; 1D5362F7EC0A627ACAB9AF4CC7F51099 /* RDReaderPagingController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderPagingController.swift; sourceTree = ""; }; 1D6DA580F40DF113709E15CF6542A35C /* DTCoreTextLayouter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextLayouter.m; path = Core/Source/DTCoreTextLayouter.m; sourceTree = ""; }; - 1DF55EE5FC41651D54FEAD1F8097F034 /* RDEPUBSelectionOverlayView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBSelectionOverlayView.swift; sourceTree = ""; }; + 1DF05A515E551E5511DBE86C4A494EF1 /* UIColor+RDEPUBHex.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "UIColor+RDEPUBHex.swift"; sourceTree = ""; }; + 1E7CF983E6CF4797FA35E3B583ECA941 /* RDEPUBReaderConfiguration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderConfiguration.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 = ""; }; + 20E5F9A5250EB5724D516BF67152CBB8 /* RDEPUBReaderController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderController.swift; sourceTree = ""; }; 2101691D5743105B2E3E40376584B91C /* DTFolderMonitor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTFolderMonitor.h; path = Core/Source/DTFolderMonitor.h; sourceTree = ""; }; 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 = ""; }; 22BDEB6AC81168F4F3FE78ACCEC2A32D /* RDEPUBWebView+FixedLayout.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBWebView+FixedLayout.swift"; sourceTree = ""; }; - 2335B9D3BB17D5D8C1AE0AF4D9365BD4 /* RDEPUBChapterRuntimeStore.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterRuntimeStore.swift; sourceTree = ""; }; 2353E8412F8D0EF503DEA9A6178F5948 /* RDEPUBParser+TOC.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBParser+TOC.swift"; sourceTree = ""; }; 238893C9CBD95CB9BDF069EB5A303E17 /* RDReaderView */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = RDReaderView; path = RDReaderView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2451995F95C8B333F972A39BDFC7EBCE /* RDEPUBReaderSearchCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderSearchCoordinator.swift; sourceTree = ""; }; 249747495DE8349E480E4789B12D366D /* SnapKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SnapKit.modulemap; sourceTree = ""; }; 25F52D9C765ED2FA87834D4310203E0B /* DTFoundation-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "DTFoundation-prefix.pch"; sourceTree = ""; }; - 266AEC0C2D6983818D5C107DEA5BB286 /* RDEPUBTextContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextContentView.swift; sourceTree = ""; }; 26A9B74B4E5F952B9DC79C10B5D8E7F0 /* RDEPUBReadingSession.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReadingSession.swift; sourceTree = ""; }; + 26C542F1825BDD5743D52350B8F4788D /* RDEPUBReaderController+PublicAPI.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBReaderController+PublicAPI.swift"; 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 = ""; }; 2883B11ACE6B06556D1B0FC8DC0F7E30 /* RDEPUBPaginationCacheCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPaginationCacheCoordinator.swift; sourceTree = ""; }; 28D62D3200AC3D4E42C81CDFF7E884E8 /* ConstraintItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintItem.swift; path = Sources/ConstraintItem.swift; sourceTree = ""; }; - 290EA639B2AE8B8092D7C08A581CD970 /* RDEPUBReaderDependencies.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderDependencies.swift; sourceTree = ""; }; + 2909100BDF5A0331D6BE22AF122BB95F /* RDEPUBTextSelectionController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextSelectionController.swift; sourceTree = ""; }; 2940F45AAC94F45A257576176011B6A4 /* RDReaderView.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = RDReaderView.release.xcconfig; sourceTree = ""; }; 2AB3159B171D3889AC3B55CCDE7A58F3 /* RDEPUBFontNormalizer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBFontNormalizer.swift; sourceTree = ""; }; 2B791CDD0D79122F60EC136C025BC751 /* RDEPUBTextPaginationInterfaces.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextPaginationInterfaces.swift; sourceTree = ""; }; 2BA19BD582C1BD54AF673014C04848B6 /* DTFoundation-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "DTFoundation-Info.plist"; sourceTree = ""; }; - 2BCA80CA6D8AE27898A0F352AA19FB31 /* RDEPUBReaderSearchBarView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderSearchBarView.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 = ""; }; - 2CAFA445DFFA6C8B61C7306D968C7595 /* RDEPUBWebContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBWebContentView.swift; 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 = ""; }; - 2F3C3B060FF4C0BF31718EDE2DB45BB9 /* RDEPUBChapterSummaryDiskCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterSummaryDiskCache.swift; sourceTree = ""; }; 306477706BBB1E784F55BCF8EB1AD737 /* Pods-ReadViewDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ReadViewDemo.debug.xcconfig"; sourceTree = ""; }; + 3068218D3A08F227DDA6216F94FB69F4 /* RDEPUBChapterDataCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterDataCache.swift; sourceTree = ""; }; 31318B9A80CC694F9EFE2A72D4826B64 /* RDEPUBTextBuildPipelineInterfaces.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextBuildPipelineInterfaces.swift; sourceTree = ""; }; 314D77A91FF74A0ACD762C92A6C1AF19 /* wxread-replace-latin.css */ = {isa = PBXFileReference; includeInIndex = 1; name = "wxread-replace-latin.css"; path = "Sources/RDReaderView/EPUBCore/Resources/wxread-replace-latin.css"; 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 = ""; }; + 32E5D40E2A0541080719F92638244887 /* RDEPUBWebContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBWebContentView.swift; sourceTree = ""; }; 32FB2B1B0498DCBE7B79BDF49E963E3D /* DTHTMLParser.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLParser.m; path = Core/Source/DTHTMLParser/DTHTMLParser.m; sourceTree = ""; }; 33DB57DF8C5AE334B1BBE9F989352F1D /* DTCustomColoredAccessory.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCustomColoredAccessory.h; path = Core/Source/iOS/DTCustomColoredAccessory.h; sourceTree = ""; }; 354C5DD0ACC742762200A640ADA859C6 /* NSCoder+DTCompatibility.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSCoder+DTCompatibility.h"; path = "Core/Source/NSCoder+DTCompatibility.h"; sourceTree = ""; }; - 356DE2A2E8D844E60004E3AAB7B524FA /* RDEPUBPageInteractionController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPageInteractionController.swift; sourceTree = ""; }; - 3636FB4CE567BE823BB3A4326A466986 /* RDEPUBReaderTableOfContentsItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderTableOfContentsItem.swift; sourceTree = ""; }; + 3562A5E853CB707C1AC4E74C3A8E3924 /* RDEPUBReaderController+ContentDelegates.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBReaderController+ContentDelegates.swift"; sourceTree = ""; }; + 357CF3DD6846FD95FE05679E6ED3866F /* RDEPUBPageLayoutSnapshot.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPageLayoutSnapshot.swift; sourceTree = ""; }; 3653FA7AD3EF76156E4FB425AC90E5F4 /* RDReaderViewProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderViewProtocols.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 = ""; }; - 3785B10F33122FC204F180B9E2366069 /* RDEPUBRuntimePageCount.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBRuntimePageCount.swift; sourceTree = ""; }; 37EFAAE86BA4FF308A74FA1CEAD40CB4 /* RDEPUBPageBreakPolicy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPageBreakPolicy.swift; sourceTree = ""; }; 3867F0F0A2292C20B5E5F8A564133F59 /* RDReaderView.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = RDReaderView.debug.xcconfig; sourceTree = ""; }; 392A75EAF280876545D14AA00517BA4C /* ConstraintMakerEditable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMakerEditable.swift; path = Sources/ConstraintMakerEditable.swift; sourceTree = ""; }; 39816EFB8DFB4FA6B91D9C8AB03A1C6A /* RDEPUBAnnotationModels.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBAnnotationModels.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 = ""; }; + 3C56140C935C7B3F792647614D702DAB /* RDEPUBChapterWindowSnapshot.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterWindowSnapshot.swift; sourceTree = ""; }; 3CCFA3D24C3438DC554760F08D6E8281 /* RDEPUBAssetRepository.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBAssetRepository.swift; sourceTree = ""; }; 3D4CD395C5569628D36C4D838AC6A8B8 /* RDEPUBParser+Package.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBParser+Package.swift"; sourceTree = ""; }; 3DB57CE3D5558A2995D7CEF1CAD00079 /* RDPlainTextBookBuilder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDPlainTextBookBuilder.swift; sourceTree = ""; }; 3DC78D0B0F37BF9AD1058CACA9AAF9BE /* Data+Serialization.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Data+Serialization.swift"; path = "Sources/ZIPFoundation/Data+Serialization.swift"; sourceTree = ""; }; - 3DDB56AC5412CF16FDBB8BF9A606E9F1 /* RDEPUBChapterLoader.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterLoader.swift; sourceTree = ""; }; + 3F2782574376AE28B6377B05472691E6 /* RDEPUBTextPageRenderView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextPageRenderView.swift; sourceTree = ""; }; 407122F95D1113F8C0E241B944F75AFE /* RDEPUBModels.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBModels.swift; sourceTree = ""; }; - 40B0AA875D62D53E865F10DC10DB4A4E /* RDEPUBPageCountCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPageCountCache.swift; sourceTree = ""; }; - 41AF383869619DE580A94B69EAC6ECF5 /* RDEPUBReaderHighlightsViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderHighlightsViewController.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 = ""; }; + 43C106392FA85206E0EE3E17BDDEFF91 /* RDEPUBReaderUIState.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderUIState.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 = ""; }; 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; }; @@ -704,12 +704,13 @@ 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; }; 4C5336F230E9CBA5516A0C4F48CC80B2 /* RDEPUBTextPerformanceSampler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextPerformanceSampler.swift; sourceTree = ""; }; 4C61D04931F7736E429371D872F7E736 /* wxread-replace.css */ = {isa = PBXFileReference; includeInIndex = 1; name = "wxread-replace.css"; path = "Sources/RDReaderView/EPUBCore/Resources/wxread-replace.css"; sourceTree = ""; }; + 4D02D5C9FBB27E031A6D934A07FB001F /* RDEPUBReaderLocationCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderLocationCoordinator.swift; sourceTree = ""; }; 4D641809A99849F80FDCB02F8CE8D6A0 /* Data+CompressionDeprecated.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Data+CompressionDeprecated.swift"; path = "Sources/ZIPFoundation/Data+CompressionDeprecated.swift"; sourceTree = ""; }; - 4E2687A6F390F43603F456693C4D2D90 /* RDEPUBReaderTheme.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderTheme.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 = ""; }; 5155CCDD61E030184249A020FE5D74B0 /* DTWeakSupport.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTWeakSupport.h; path = Core/Source/DTWeakSupport.h; sourceTree = ""; }; + 51A0D2BE0587C3DDC57DDBDCD73F81BD /* RDEPUBSelectionOverlayView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBSelectionOverlayView.swift; sourceTree = ""; }; 5245010774F8F1C4857763CC7B96E7B4 /* ConstraintMakerExtendable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMakerExtendable.swift; path = Sources/ConstraintMakerExtendable.swift; sourceTree = ""; }; 52B36F32273C511FB9B6C51CE36A6DD9 /* RDEPUBNavigatorLayoutContext.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBNavigatorLayoutContext.swift; sourceTree = ""; }; 533CFFF04BE7BC4BDEAE11DF675A84CA /* DTBreakHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTBreakHTMLElement.h; path = Core/Source/DTBreakHTMLElement.h; sourceTree = ""; }; @@ -726,18 +727,17 @@ 579711B0A35AF68319DBD269278F32FC /* RDReaderView+CollectionView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDReaderView+CollectionView.swift"; sourceTree = ""; }; 57E4EDBF01B37189D38063CF62178D33 /* RDEPUBResourceResolver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBResourceResolver.swift; sourceTree = ""; }; 587454250340EBB39CD77123D727F116 /* RDReaderView+PageCurl.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDReaderView+PageCurl.swift"; sourceTree = ""; }; - 591427AC1FAE583EB7EAC76B1E673CDA /* RDEPUBReaderSettings.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderSettings.swift; sourceTree = ""; }; - 5921C735CE80E2A8BB3B05A1914CCC16 /* RDEPUBReaderToolView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderToolView.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 = ""; }; 59AB14F9464EEFD112E2C56DF09B002D /* RDEPUBTextRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextRenderer.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 = ""; }; + 5B03892CC4EE09B2E429D0F06E2296BD /* String+SHA256.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "String+SHA256.swift"; sourceTree = ""; }; + 5B6A71ED4237A07E81D0597E56658C5A /* RDEPUBBackgroundTrace.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBBackgroundTrace.swift; sourceTree = ""; }; 5BA251BAC8B430B3A4D4C4321046B990 /* Archive+Deprecated.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Archive+Deprecated.swift"; path = "Sources/ZIPFoundation/Archive+Deprecated.swift"; sourceTree = ""; }; - 5C7E7F66A26C2FC81CA8C11585A91C6C /* RDEPUBPageLayoutSnapshot.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPageLayoutSnapshot.swift; sourceTree = ""; }; + 5CA26E2111C84E605445082FAD421636 /* RDEPUBReaderChapterListController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderChapterListController.swift; sourceTree = ""; }; 5D62C5531667F364F14DB67D33FFA554 /* DTCoreTextParagraphStyle.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextParagraphStyle.h; path = Core/Source/DTCoreTextParagraphStyle.h; sourceTree = ""; }; 5DA1E8494B86FFD79C639DCB8C21E1BB /* DTPieProgressIndicator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTPieProgressIndicator.m; path = Core/Source/iOS/DTPieProgressIndicator.m; sourceTree = ""; }; - 5DC3AB46FAF0445813FD5AFA0E66194C /* RDEPUBReaderController+ContentDelegates.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBReaderController+ContentDelegates.swift"; sourceTree = ""; }; 5E791886E59DC0FAB02C6BBAB5BE5B6B /* DTCoreTextFontCollection.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextFontCollection.m; path = Core/Source/DTCoreTextFontCollection.m; 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 = ""; }; @@ -745,19 +745,20 @@ 5FAB3C7F19A511C2B1B7DFBA3CA308C4 /* DTFoundation-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "DTFoundation-umbrella.h"; sourceTree = ""; }; 5FD2E18DC68BCC843FF378E84642CE78 /* RDEPUBFixedLayoutTemplate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBFixedLayoutTemplate.swift; sourceTree = ""; }; 5FE003BF0CD443CC90388E203EEF3FC8 /* RDEPUBParser+ReadingProfile.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBParser+ReadingProfile.swift"; sourceTree = ""; }; + 600151C0D6D719C385CC4D3FCA6E0B27 /* RDEPUBTextPageDecorationView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextPageDecorationView.swift; sourceTree = ""; }; 6014DA7213E4687224F2BF921E30002C /* DTFoundation.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = DTFoundation.debug.xcconfig; sourceTree = ""; }; 60657D08CCDB38BFED30BD40F4720CE4 /* ConstraintMaker.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMaker.swift; path = Sources/ConstraintMaker.swift; sourceTree = ""; }; + 60F861867F08812A37E8DDCBB02DC583 /* RDEPUBReaderPersistence.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderPersistence.swift; sourceTree = ""; }; + 61CAA3B6C9BC1F1E4C35A108D76B1E79 /* RDEPUBReaderBottomToolView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderBottomToolView.swift; sourceTree = ""; }; 61E36C4FE448DDDA03450847FCB785B6 /* Archive+BackingConfiguration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Archive+BackingConfiguration.swift"; path = "Sources/ZIPFoundation/Archive+BackingConfiguration.swift"; sourceTree = ""; }; 63A6EDF10DA5A051C6CDCBE5D97AF11E /* DTVideoTextAttachment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTVideoTextAttachment.m; path = Core/Source/DTVideoTextAttachment.m; 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 = ""; }; - 6567762A90252DF61DBD13E3CCB5BF52 /* RDEPUBReaderLoadCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderLoadCoordinator.swift; sourceTree = ""; }; 6578DFAB51436E8DF266FD087700E26B /* ConstraintDescription.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintDescription.swift; path = Sources/ConstraintDescription.swift; sourceTree = ""; }; - 6714862E1752548842941DFE880513C0 /* RDEPUBReaderSettingsViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderSettingsViewController.swift; sourceTree = ""; }; - 68C5ACCC99754FDAFCC9F78F9163AC5E /* RDEPUBReaderPersistence.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderPersistence.swift; sourceTree = ""; }; 6A1EBB9497F4846C84CA0BF5E4F84FC7 /* DTLog.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTLog.h; path = Core/Source/DTLog.h; sourceTree = ""; }; - 6ABEFE84724594A5A9040F3C67BAFD3B /* RDEPUBBackgroundTrace.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBBackgroundTrace.swift; sourceTree = ""; }; + 6B1E75CAB3F7C45A794F6C5335FFECBB /* RDEPUBReaderTheme.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderTheme.swift; sourceTree = ""; }; + 6B3C0665F2995A9C45FCAABC3F62DE3D /* RDEPUBReaderDependencies.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderDependencies.swift; sourceTree = ""; }; 6B4DD7F34461088F19AB93FBE13CC9BD /* RDEPUBWebViewDebug.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBWebViewDebug.swift; sourceTree = ""; }; 6B96B0998B49D8E149C995474DA5FE5F /* DTHTMLWriter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTHTMLWriter.h; path = Core/Source/DTHTMLWriter.h; sourceTree = ""; }; 6C337B19D3E4AFBDAEB7B261AA881E5C /* RDEPUBHTMLNormalizer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBHTMLNormalizer.swift; sourceTree = ""; }; @@ -776,28 +777,23 @@ 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 = ""; }; - 7516CFAD05F8C77E5C44B21BAA4E0FD2 /* RDEPUBTextPageRenderView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextPageRenderView.swift; sourceTree = ""; }; 752EEC76100E31C66D33F087A318A0B8 /* wxread-default.css */ = {isa = PBXFileReference; includeInIndex = 1; name = "wxread-default.css"; path = "Sources/RDReaderView/EPUBCore/Resources/wxread-default.css"; sourceTree = ""; }; 75C5986EF2D63801A8A30B9D77A7FDD0 /* ResourceBundle-ZIPFoundation_Privacy-ZIPFoundation-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-ZIPFoundation_Privacy-ZIPFoundation-Info.plist"; sourceTree = ""; }; 75FA8E843D9225BD65EB9D17825EC0F4 /* NSCharacterSet+HTML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSCharacterSet+HTML.h"; path = "Core/Source/NSCharacterSet+HTML.h"; 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 = ""; }; - 778702DC79D5849F3C4DF790310D0917 /* RDEPUBReaderBottomToolView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderBottomToolView.swift; sourceTree = ""; }; 77A613841EB90827AF321DD50B21DB5D /* DTAttributedTextView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAttributedTextView.m; path = Core/Source/DTAttributedTextView.m; 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; }; 79A77584AB3B92B2508AE0BCFCB8FD87 /* UIView+DTFoundation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+DTFoundation.h"; path = "Core/Source/iOS/UIView+DTFoundation.h"; sourceTree = ""; }; 7A9AB5C9C4F800F35807FC8C8C016D37 /* RDEPUBWebView+Search.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBWebView+Search.swift"; sourceTree = ""; }; 7AB9016408894675D11069CDAACEDE94 /* DTImage+HTML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DTImage+HTML.h"; path = "Core/Source/DTImage+HTML.h"; sourceTree = ""; }; - 7BB3C8634528BC5B2BC8EA70F1FD14FE /* RDEPUBReaderController+PublicAPI.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBReaderController+PublicAPI.swift"; sourceTree = ""; }; + 7AD6EF544C3C1F037842DF5E4CD048B0 /* RDEPUBReaderSettingsViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderSettingsViewController.swift; sourceTree = ""; }; 7C496AB6A73C2D0BD0FF340A6A2960D5 /* DTHTMLAttributedStringBuilder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLAttributedStringBuilder.m; path = Core/Source/DTHTMLAttributedStringBuilder.m; sourceTree = ""; }; - 7CBD2948DE6941D36BAF37A94CC7981A /* RDEPUBReaderController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderController.swift; sourceTree = ""; }; 7D63AE1801C7AB149219317BE7529B26 /* DTAnchorHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAnchorHTMLElement.m; path = Core/Source/DTAnchorHTMLElement.m; 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 = ""; }; - 7F278454696A046C7789CF994A17CE53 /* RDEPUBReaderController+RuntimeBridge.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBReaderController+RuntimeBridge.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 = ""; }; - 81EAF121C946325DF0FA84458CDFA926 /* RDEPUBWebDecorationOverlayView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBWebDecorationOverlayView.swift; sourceTree = ""; }; 8272D0B59ABD6652D0CAE3DFCC2C5EE1 /* DTActivityTitleView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTActivityTitleView.h; path = Core/Source/iOS/DTActivityTitleView.h; sourceTree = ""; }; 82FE20C2FB684C45A8135A774F9EA2CA /* RDEPUBSearchEngine.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBSearchEngine.swift; sourceTree = ""; }; 83410CC9CF2ABE63B90A92F2F988BF65 /* RDReaderView-RDReaderViewAssets */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "RDReaderView-RDReaderViewAssets"; path = RDReaderViewAssets.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -807,8 +803,8 @@ 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 = ""; }; + 88DC78A6C33BEF2BC4ADB375F162C284 /* RDEPUBReaderAnnotationCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderAnnotationCoordinator.swift; sourceTree = ""; }; 8967AE46A1F72D8F08E5B81E1FE83ADA /* RDEPUBTextSearchEngine.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextSearchEngine.swift; sourceTree = ""; }; - 89F87AEC289B931D4CA74AD9BACE1736 /* RDEPUBReaderSearchCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderSearchCoordinator.swift; sourceTree = ""; }; 8A3DD09EADFE152D5050A931432C2C60 /* RDEPUBStyleSheetComposer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBStyleSheetComposer.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 = ""; }; @@ -817,21 +813,25 @@ 8BD9F2023FF826F67E69E3407A70A8A6 /* DTVersion.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTVersion.h; path = Core/Source/DTVersion.h; sourceTree = ""; }; 8C7CBC59E42E8E03559CFEE4F14A56AC /* RDReaderView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderView.swift; sourceTree = ""; }; 8CFEE298DD52246B46E6FA62B16B38DF /* DTTextHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTTextHTMLElement.m; path = Core/Source/DTTextHTMLElement.m; sourceTree = ""; }; + 8DE6D61DD1D70A6DDF12430979C4082A /* RDEPUBReaderViewportMonitor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderViewportMonitor.swift; 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 = ""; }; 8EC834DF4FD9ABC75E1FDED3BBCB37B0 /* RDEPUBNavigatorState.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBNavigatorState.swift; sourceTree = ""; }; - 8F01C26E4C66501E3CEB74C752FA77D0 /* RDEPUBReaderViewportMonitor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderViewportMonitor.swift; sourceTree = ""; }; 8F2D44B82815CA3397BE2C9F64819B88 /* DTCoreTextFunctions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextFunctions.m; path = Core/Source/DTCoreTextFunctions.m; sourceTree = ""; }; 8F950CEF31AC0065C0BEAB26BA2CEFA1 /* ConstraintOffsetTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintOffsetTarget.swift; path = Sources/ConstraintOffsetTarget.swift; sourceTree = ""; }; + 902D8268D209ABBB2768E10905B189C2 /* RDEPUBReaderTableOfContentsItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderTableOfContentsItem.swift; sourceTree = ""; }; 91144FC099DD458E61625CD939577DAA /* ConstraintDirectionalInsets.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintDirectionalInsets.swift; path = Sources/ConstraintDirectionalInsets.swift; sourceTree = ""; }; 9129D37C7F89AD8FD530663BB4503FC0 /* NSCoder+DTCompatibility.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSCoder+DTCompatibility.m"; path = "Core/Source/NSCoder+DTCompatibility.m"; sourceTree = ""; }; + 91EAE48AF0DC04CF719A938AD5613EC3 /* RDEPUBChapterCacheKey.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterCacheKey.swift; sourceTree = ""; }; 923C96CA59176956C63098AF00B0201B /* RDEPUBTextPaginationSupport.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextPaginationSupport.swift; sourceTree = ""; }; 92460D196C5300656C9649784EBD3C58 /* NSAttributedString+HTML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSAttributedString+HTML.m"; path = "Core/Source/NSAttributedString+HTML.m"; sourceTree = ""; }; + 9255AF737FE4AA3AE50EE4F2BDCC9AD7 /* RDEPUBReaderTopToolView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderTopToolView.swift; sourceTree = ""; }; 932FD688DE493323BA6B691BC2CB5094 /* LayoutConstraint.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayoutConstraint.swift; path = Sources/LayoutConstraint.swift; sourceTree = ""; }; 934F9BB2B3A313294F121484C3A97A14 /* Archive+ReadingDeprecated.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Archive+ReadingDeprecated.swift"; path = "Sources/ZIPFoundation/Archive+ReadingDeprecated.swift"; sourceTree = ""; }; 937CEFA81DA4227008DBFB114783A08B /* SSAlertSwift-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SSAlertSwift-umbrella.h"; sourceTree = ""; }; 93898CAF537398E00D8113217C419CA9 /* RDReaderView+ToolView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDReaderView+ToolView.swift"; sourceTree = ""; }; 945D4549E0B9168D7DDB910DE874B874 /* Typealiases.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Typealiases.swift; path = Sources/Typealiases.swift; sourceTree = ""; }; + 94798D2C9B825493840EF99A7447AFB7 /* RDEPUBReaderRuntime.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderRuntime.swift; sourceTree = ""; }; 957DB4675D5CCBD3CB1762F08619F3F7 /* UIApplication+DTNetworkActivity.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIApplication+DTNetworkActivity.h"; path = "Core/Source/iOS/UIApplication+DTNetworkActivity.h"; sourceTree = ""; }; 96EEB6B022E22A4ABC86D87469D4815B /* DTSmartPagingScrollView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTSmartPagingScrollView.m; path = Core/Source/iOS/DTSmartPagingScrollView.m; sourceTree = ""; }; 96FA99AA7D4AD0D4246C8DAC1CC0C24E /* DTStylesheetHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTStylesheetHTMLElement.h; path = Core/Source/DTStylesheetHTMLElement.h; sourceTree = ""; }; @@ -839,11 +839,12 @@ 97995C73297871C353364E685939DED9 /* SSAlertAnimationController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SSAlertAnimationController.swift; path = Sources/SSAlertSwift/SSAlertAnimationController.swift; sourceTree = ""; }; 982391EC8620A617B8788C8A6101C96A /* DTDictationPlaceholderView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTDictationPlaceholderView.h; path = Core/Source/DTDictationPlaceholderView.h; sourceTree = ""; }; 98658510AA525496EBF5792E5D9884ED /* DTListItemHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTListItemHTMLElement.h; path = Core/Source/DTListItemHTMLElement.h; sourceTree = ""; }; + 9899192BA37086232FFEA93F5B2EC3B3 /* RDEPUBViewportTypes.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBViewportTypes.swift; sourceTree = ""; }; 98C7359A7C3FC7675FED220200A43A17 /* Archive+WritingDeprecated.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Archive+WritingDeprecated.swift"; path = "Sources/ZIPFoundation/Archive+WritingDeprecated.swift"; sourceTree = ""; }; 99158A091EAB840DE3F822364077954D /* RDEPUBPreferences.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPreferences.swift; sourceTree = ""; }; 99299A9FD6EF739D18352AC5B9325CB0 /* DTAccessibilityElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTAccessibilityElement.h; path = Core/Source/DTAccessibilityElement.h; sourceTree = ""; }; + 9A0C8E542EFB0872BF4471EB2EDB06EA /* RDEPUBReaderSettings.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderSettings.swift; sourceTree = ""; }; 9A1B75050E71BDD96FC19EC6BA129E6A /* DTLinkButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTLinkButton.m; path = Core/Source/DTLinkButton.m; sourceTree = ""; }; - 9B81846EA13E575E1459B41F96ACB244 /* RDEPUBReaderConfiguration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderConfiguration.swift; sourceTree = ""; }; 9BFCAC7C2AEA867E67C63A852576774F /* NSURL+DTComparing.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSURL+DTComparing.m"; path = "Core/Source/NSURL+DTComparing.m"; sourceTree = ""; }; 9C6293E97E128E98559FB74D9A342EAD /* NSMutableString+HTML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSMutableString+HTML.h"; path = "Core/Source/NSMutableString+HTML.h"; sourceTree = ""; }; 9CE078B465FDEF40EC0469626A2C2469 /* SnapKit-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SnapKit-dummy.m"; sourceTree = ""; }; @@ -865,7 +866,6 @@ A3565FEE19F19CB43A43409AFFF2BD2C /* NSString+DTPaths.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+DTPaths.m"; path = "Core/Source/NSString+DTPaths.m"; sourceTree = ""; }; A3765B289CB7E6C5DF6160D8EBC83C3C /* UIScreen+DTFoundation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIScreen+DTFoundation.h"; path = "Core/Source/iOS/UIScreen+DTFoundation.h"; sourceTree = ""; }; A4E43DCDE0C8751557E7E65FEEB1C865 /* DTLazyImageView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTLazyImageView.m; path = Core/Source/DTLazyImageView.m; sourceTree = ""; }; - A5192CA781CBC54A3FDB730E48E1F25A /* RDEPUBPageResolver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPageResolver.swift; sourceTree = ""; }; A651CEE8062C84A597DBC44ECA3DBCC1 /* ConstraintDirectionalInsetTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintDirectionalInsetTarget.swift; path = Sources/ConstraintDirectionalInsetTarget.swift; sourceTree = ""; }; A68D72764140BC3DF67A2B9E52CEC7A4 /* DTHorizontalRuleHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHorizontalRuleHTMLElement.m; path = Core/Source/DTHorizontalRuleHTMLElement.m; sourceTree = ""; }; A8DFDCAB69B471EB5667C964865C26CF /* DTCoreGraphicsUtils.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreGraphicsUtils.m; path = Core/Source/DTCoreGraphicsUtils.m; sourceTree = ""; }; @@ -876,9 +876,13 @@ AAF8513093523204F2598DC45941FB49 /* RDEPUBTextIndexTable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextIndexTable.swift; sourceTree = ""; }; AD987C2FA7C8FBC96FD024E421DA0E68 /* Archive+Progress.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Archive+Progress.swift"; path = "Sources/ZIPFoundation/Archive+Progress.swift"; sourceTree = ""; }; ADC5AC8FD4EC45E9BA5654A38715BB93 /* DTTextBlock.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTTextBlock.m; path = Core/Source/DTTextBlock.m; sourceTree = ""; }; + AE57AC7D620527A77DEF6EB1162C9DFC /* RDEPUBChapterOffsetMap.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterOffsetMap.swift; sourceTree = ""; }; AE68175CDCB5EB0A5693C5DD9DD07BFA /* RDEPUBWebView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBWebView.swift; sourceTree = ""; }; + AE6AED7CB30C4F5DF029315D9602ABE2 /* RDEPUBReaderSearchBarView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderSearchBarView.swift; sourceTree = ""; }; AFC4C9F69BF2D0CAEBAA83896C139D2C /* DTColorFunctions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTColorFunctions.m; path = Core/Source/DTColorFunctions.m; sourceTree = ""; }; AFE0B56638AFB77F4455784A7ED59876 /* DTDictationPlaceholderView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTDictationPlaceholderView.m; path = Core/Source/DTDictationPlaceholderView.m; sourceTree = ""; }; + B02BB9570D018CF824C0024AEDA9DF92 /* RDEPUBChapterWindowCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterWindowCoordinator.swift; sourceTree = ""; }; + B04B186DAA39C121040AC3B1018D12E8 /* RDEPUBReaderController+DataSource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBReaderController+DataSource.swift"; sourceTree = ""; }; B0500CA69405E23B09291359EB28331E /* NSMutableAttributedString+HTML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSMutableAttributedString+HTML.m"; path = "Core/Source/NSMutableAttributedString+HTML.m"; sourceTree = ""; }; B0AC307F12521444C973EA0DE38B4608 /* NSMutableArray+DTMoving.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSMutableArray+DTMoving.h"; path = "Core/Source/NSMutableArray+DTMoving.h"; sourceTree = ""; }; B0B32EA4B7A492EEF26CE260E9AEFA0B /* ConstraintMakerFinalizable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMakerFinalizable.swift; path = Sources/ConstraintMakerFinalizable.swift; sourceTree = ""; }; @@ -891,11 +895,13 @@ B5FCE04EBB553439D1C2BA1001936293 /* DTCompatibility.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCompatibility.h; path = Core/Source/DTCompatibility.h; sourceTree = ""; }; B6425B2235BA00FE865AF58EF44F1144 /* DTCoreGraphicsUtils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreGraphicsUtils.h; path = Core/Source/DTCoreGraphicsUtils.h; sourceTree = ""; }; B7672A26922DBD934FA1AC60D49EB322 /* NSNumber+RomanNumerals.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSNumber+RomanNumerals.m"; path = "Core/Source/NSNumber+RomanNumerals.m"; sourceTree = ""; }; + B8CC24C31135ADAF3F1271D5BA167262 /* RDEPUBRuntimeChapter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBRuntimeChapter.swift; sourceTree = ""; }; B8E40E302B85493BDE0FE91F829EC01E /* DTBase64Coding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTBase64Coding.h; path = Core/Source/DTBase64Coding.h; sourceTree = ""; }; B9DCB5EC0B1CDADD221717CADDF62359 /* SnapKit-SnapKit_Privacy */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "SnapKit-SnapKit_Privacy"; path = SnapKit_Privacy.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; B9E40164920C9AC1B1413C51835CD370 /* DTCoreText-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "DTCoreText-Info.plist"; sourceTree = ""; }; - B9EF15D29CE2E35CD8D3439E3C91EEC9 /* RDEPUBChapterLocation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterLocation.swift; sourceTree = ""; }; + BB8687FD6159F07520A97D89A4A739AB /* RDEPUBChapterLoader.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterLoader.swift; sourceTree = ""; }; BBD9F7B0D64CCCDACB7D703E942A4EBE /* RDEPUBSemanticMarkerInjector.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBSemanticMarkerInjector.swift; sourceTree = ""; }; + BC30482E1E56725E225659BE42ED79EC /* RDEPUBTextContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextContentView.swift; sourceTree = ""; }; BC332335B380BC51C4F05C5CCDFB9189 /* DTAnimatedGIF.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAnimatedGIF.m; path = Core/Source/iOS/DTAnimatedGIF/DTAnimatedGIF.m; sourceTree = ""; }; BC631FFAFFA79930F8DB8AF60D629C5E /* RDReaderView.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; path = RDReaderView.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; BCCA590DB72AA93275262A414F16578A /* DTCoreTextFontDescriptor.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextFontDescriptor.m; path = Core/Source/DTCoreTextFontDescriptor.m; sourceTree = ""; }; @@ -903,11 +909,10 @@ BDD081272B47A3B73CE693D21DC26827 /* Entry.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Entry.swift; path = Sources/ZIPFoundation/Entry.swift; sourceTree = ""; }; BE547E464E5D34F9FFB24BAC81D1E4CC /* NSString+DTUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+DTUtilities.h"; path = "Core/Source/NSString+DTUtilities.h"; sourceTree = ""; }; BF4F3EC137168B92A5DA85AF50CD239F /* NSAttributedString+DTDebug.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSAttributedString+DTDebug.m"; path = "Core/Source/NSAttributedString+DTDebug.m"; sourceTree = ""; }; - C0281BCEC7C2071D2996CBAC72E78DC2 /* RDEPUBReaderController+RenderSupport.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBReaderController+RenderSupport.swift"; sourceTree = ""; }; + BF725A63AD2B9E43F33EB99F07C304D4 /* RDEPUBSelectionState.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBSelectionState.swift; sourceTree = ""; }; C123C127CC539ACCB510F665DB774899 /* DTAttributedTextContentView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTAttributedTextContentView.h; path = Core/Source/DTAttributedTextContentView.h; sourceTree = ""; }; C18480913E002315E3F5EC6945371697 /* NSDictionary+DTError.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+DTError.m"; path = "Core/Source/NSDictionary+DTError.m"; sourceTree = ""; }; C1CD5FB30A34088E10667AF4D2A8A6C3 /* RDEPUBReadingLocationModels.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReadingLocationModels.swift; sourceTree = ""; }; - C1E1631C6F8CBDBBDDC1DC595AF83F4E /* RDEPUBBookPageMap.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBBookPageMap.swift; sourceTree = ""; }; C241AD9CEC9D6732CFBA6A55B49E64C9 /* ConstraintInsets.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintInsets.swift; path = Sources/ConstraintInsets.swift; sourceTree = ""; }; C26DBE1150BC9A30B69839783E000358 /* DTCoreTextLayouter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextLayouter.h; path = Core/Source/DTCoreTextLayouter.h; sourceTree = ""; }; C2840BE6D91479CCBC2E908FBD9DA183 /* DTCoreText.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = DTCoreText.release.xcconfig; sourceTree = ""; }; @@ -917,7 +922,6 @@ C4C7097CA92DB08445EE4D912804A41B /* wxread-dark.css */ = {isa = PBXFileReference; includeInIndex = 1; name = "wxread-dark.css"; path = "Sources/RDReaderView/EPUBCore/Resources/wxread-dark.css"; sourceTree = ""; }; C5B4E39C9AFC826DEBB56E697B1F4FBE /* RDEPUBSearchModels.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBSearchModels.swift; sourceTree = ""; }; C5D1621642B6C2702FFE6606483DEF37 /* RDEPUBChapterData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterData.swift; sourceTree = ""; }; - C68BF07B565C3B6919A14BD1973D2AEE /* UIColor+RDEPUBHex.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "UIColor+RDEPUBHex.swift"; sourceTree = ""; }; C6DB34ACB132E465CDF8191F7DB71EF6 /* ConstraintView+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ConstraintView+Extensions.swift"; path = "Sources/ConstraintView+Extensions.swift"; sourceTree = ""; }; C71D83D6AEF198FA680B8A80BF2D9605 /* DTCoreTextLayoutLine.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextLayoutLine.m; path = Core/Source/DTCoreTextLayoutLine.m; sourceTree = ""; }; C80065DBA5634DAA71129404975D7349 /* NSURL+DTUnshorten.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSURL+DTUnshorten.m"; path = "Core/Source/NSURL+DTUnshorten.m"; sourceTree = ""; }; @@ -927,42 +931,43 @@ C9593E7B37569219F8D9A504B47707DC /* ConstraintAttributes.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintAttributes.swift; path = Sources/ConstraintAttributes.swift; sourceTree = ""; }; C960B7AA0B9B8CC473FACFEC64F9906D /* SnapKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SnapKit.debug.xcconfig; sourceTree = ""; }; C9B5CCE470E634B18B32B423369C1A31 /* RDEPUBCoreTextPageFrameFactory.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBCoreTextPageFrameFactory.swift; sourceTree = ""; }; - C9F07C5EDFE63EB17570FE17E8E6E1B8 /* RDEPUBLocationConverter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBLocationConverter.swift; sourceTree = ""; }; + CA3ACBCB65E911E12245EDA437DF3088 /* RDEPUBReaderAssemblyCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderAssemblyCoordinator.swift; sourceTree = ""; }; CA4A114775B98CEA4A566C18123B145F /* ZIPFoundation-ZIPFoundation_Privacy */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "ZIPFoundation-ZIPFoundation_Privacy"; path = ZIPFoundation_Privacy.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; CA538A48DD3691701EC6B5021D7B7381 /* NSScanner+HTML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSScanner+HTML.m"; path = "Core/Source/NSScanner+HTML.m"; sourceTree = ""; }; CB7F3535507F56824BA21509302CF8B3 /* NSString+DTURLEncoding.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+DTURLEncoding.m"; path = "Core/Source/NSString+DTURLEncoding.m"; sourceTree = ""; }; CBD671BD778F560BB9C93218055756E1 /* RDEPUBWebView+Reflowable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBWebView+Reflowable.swift"; sourceTree = ""; }; - CC7081E8A3F5A8A212D8F11DA4582929 /* RDEPUBChapterCacheKey.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterCacheKey.swift; sourceTree = ""; }; + CBF22F617BFAC4143D2661C887ACACDB /* RDEPUBTextAnnotationOverlay.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextAnnotationOverlay.swift; sourceTree = ""; }; + CD0D4092A12656EBCF1A6F72B4FD4082 /* RDEPUBReaderLoadCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderLoadCoordinator.swift; sourceTree = ""; }; CE6907AC04870132CCE3177E894607DD /* DTCoreTextLayoutFrame.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextLayoutFrame.h; path = Core/Source/DTCoreTextLayoutFrame.h; sourceTree = ""; }; CE7768E097F8BDE9FBE3752B98CD0101 /* DTCoreText-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "DTCoreText-umbrella.h"; sourceTree = ""; }; CEA93EDA6ED54D374265AA5D6B9EBBA9 /* NSArray+DTError.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSArray+DTError.h"; path = "Core/Source/NSArray+DTError.h"; sourceTree = ""; }; + CFBCC74BCB4330EB8EF1B57BADBB014B /* RDEPUBWebDecorationOverlayView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBWebDecorationOverlayView.swift; sourceTree = ""; }; D079A146A6FF29F12B0747422C2EA97F /* RDReaderView+ContentAccess.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDReaderView+ContentAccess.swift"; sourceTree = ""; }; D1599698B5A62671C488F13E07272B09 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextLayoutFrameAccessibilityElementGenerator.m; path = Core/Source/DTCoreTextLayoutFrameAccessibilityElementGenerator.m; sourceTree = ""; }; D1E89A31867FF1FB44B4B999988F1B82 /* DTFoundation */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = DTFoundation; path = DTFoundation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D21EFC0B1DC22A993EB8B62715E87FC0 /* RDEPUBRuntimePageCount.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBRuntimePageCount.swift; sourceTree = ""; }; D24FE8634A7AD2562DB8ADB9D4BBB36A /* CTLineUtils.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = CTLineUtils.m; path = Core/Source/CTLineUtils.m; sourceTree = ""; }; D2858C4311A2E3D5C75501279AD0FFE5 /* RDEPUBDTCoreTextRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBDTCoreTextRenderer.swift; sourceTree = ""; }; D2BA6D5DBD4C6A9285C4592721790A26 /* DTSmartPagingScrollView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTSmartPagingScrollView.h; path = Core/Source/iOS/DTSmartPagingScrollView.h; sourceTree = ""; }; + D2C33E5B1677A732781A827A0A58805F /* RDEPUBChapterSummaryDiskCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterSummaryDiskCache.swift; sourceTree = ""; }; D3593ADF561D422F73C8EABAD4A4F440 /* ConstraintRelation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintRelation.swift; path = Sources/ConstraintRelation.swift; sourceTree = ""; }; D36ED8209C907E3E9B074DDB399F54FF /* DTTextAttachment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTTextAttachment.h; path = Core/Source/DTTextAttachment.h; sourceTree = ""; }; D3C42316556BF2EEC09C00E6296E6B2D /* RDEPUBStyleSheetBuilder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBStyleSheetBuilder.swift; sourceTree = ""; }; D4093D30B372960168C1349100DF7DE0 /* DTAttributedTextCell.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTAttributedTextCell.h; path = Core/Source/DTAttributedTextCell.h; sourceTree = ""; }; D495E2AB1223BD4D4909F8FF1FF47DAF /* Debugging.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Debugging.swift; path = Sources/Debugging.swift; sourceTree = ""; }; D497C3CA364E37DAAFF2C3B90CC7DBE8 /* DTTextBlock.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTTextBlock.h; path = Core/Source/DTTextBlock.h; sourceTree = ""; }; - D4FF37961C00CE5162EE9165F6EC5E1A /* RDURLReaderController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDURLReaderController.swift; sourceTree = ""; }; + D4D1E00E7B5F60E25B326CF4B83E22B2 /* RDEPUBBookPageMap.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBBookPageMap.swift; sourceTree = ""; }; D5CD25E951901E42A8D31765763F1CED /* RDReaderGestureController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderGestureController.swift; sourceTree = ""; }; - D5F6D9DC5EAEE10F7D8644F06CEE3C99 /* String+SHA256.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "String+SHA256.swift"; sourceTree = ""; }; D6D4CF544288387FDB24F07552DDE550 /* RDEPUBFragmentMarkerInjector.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBFragmentMarkerInjector.swift; sourceTree = ""; }; - D6E097E98410554175AFEAF5BF4CC963 /* RDEPUBReaderPaginationCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderPaginationCoordinator.swift; sourceTree = ""; }; - D74BFC8E9133BF4E551E3A93798B4F99 /* RDEPUBReaderChapterListController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderChapterListController.swift; sourceTree = ""; }; + D7A3D6CCECEDECBA9575590F2349E274 /* RDEPUBReaderDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderDelegate.swift; sourceTree = ""; }; D7B5E61166782ED8D585D4FCEE2074DB /* DTLinkButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTLinkButton.h; path = Core/Source/DTLinkButton.h; sourceTree = ""; }; + D7BBD50992F22841D962895AED7442C7 /* RDEPUBChapterLocation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterLocation.swift; sourceTree = ""; }; D80CD20CB452B31CCCB14AF8567524D3 /* DTCoreText-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "DTCoreText-prefix.pch"; sourceTree = ""; }; D85A85D891A320165843EF9F489178F7 /* UIImage+DTFoundation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+DTFoundation.m"; path = "Core/Source/iOS/UIImage+DTFoundation.m"; sourceTree = ""; }; D863AE4F659A0F71B22DC9FF334DDF4E /* DTCoreTextLayoutFrameAccessibilityElementGenerator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextLayoutFrameAccessibilityElementGenerator.h; path = Core/Source/DTCoreTextLayoutFrameAccessibilityElementGenerator.h; sourceTree = ""; }; D8C351514678D41C58869BD057D5483F /* UIViewFrameExtension.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UIViewFrameExtension.swift; path = Sources/SSAlertSwift/UIViewFrameExtension.swift; sourceTree = ""; }; - D8D9882EB39FEC92F33CDC352AF14F3E /* RDEPUBReaderTopToolView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderTopToolView.swift; sourceTree = ""; }; D8EED36C051D89B37BBEB069A27F2099 /* RDEPUBPublication.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPublication.swift; sourceTree = ""; }; D927F128EBF26AD2C61FD0E6B6F6F5ED /* ZIPFoundation-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ZIPFoundation-umbrella.h"; sourceTree = ""; }; - D96987335475BD8BA2521AFC376AA33C /* RDEPUBTextSelectionController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextSelectionController.swift; sourceTree = ""; }; D9AFC4B66F872436A059FE53780D216F /* SSAlertSwift */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SSAlertSwift; path = SSAlertSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DA25968B7C9A40031DA45BB328EA8A2E /* DTStylesheetHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTStylesheetHTMLElement.m; path = Core/Source/DTStylesheetHTMLElement.m; sourceTree = ""; }; DA5D38D76DC2B327F6FA86DD38AD5079 /* rangy-core.js */ = {isa = PBXFileReference; includeInIndex = 1; name = "rangy-core.js"; path = "Sources/RDReaderView/EPUBCore/Resources/rangy-core.js"; sourceTree = ""; }; @@ -973,12 +978,12 @@ DD189DA6054AAE2242F6644A2623B50E /* NSAttributedString+SmallCaps.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSAttributedString+SmallCaps.h"; path = "Core/Source/NSAttributedString+SmallCaps.h"; sourceTree = ""; }; DD58A1908F3C532210EA65ACE410A44D /* CTLineUtils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CTLineUtils.h; path = Core/Source/CTLineUtils.h; sourceTree = ""; }; DE4821B98DDE6C71411EC139EC6D2762 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; - DE5CC526B804F5385241DD0D5A71831E /* RDEPUBReaderRuntime.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderRuntime.swift; sourceTree = ""; }; DEF667FF45F147B7458680F3808B36A4 /* RDEPUBResourceURLSchemeHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBResourceURLSchemeHandler.swift; sourceTree = ""; }; DF5EDCA3B21964D0462BE0982E2F0BF8 /* DTTiledLayerWithoutFade.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTTiledLayerWithoutFade.h; path = Core/Source/iOS/DTTiledLayerWithoutFade.h; sourceTree = ""; }; DF670EC438F05ACC1F1C7B725DBDAA18 /* DTFoundation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = DTFoundation.release.xcconfig; sourceTree = ""; }; DF988440049800DB7824A94B81D20254 /* Archive+Writing.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Archive+Writing.swift"; path = "Sources/ZIPFoundation/Archive+Writing.swift"; sourceTree = ""; }; DFA800320B45D4A508A1F6E216061675 /* RDReaderView-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "RDReaderView-Info.plist"; sourceTree = ""; }; + E088FB8503A3AEBBB051817E55410CAE /* RDEPUBReaderPaginationCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderPaginationCoordinator.swift; sourceTree = ""; }; E0D90EB06FC37D534934542F9CAB1D74 /* DTCSSStylesheet.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCSSStylesheet.m; path = Core/Source/DTCSSStylesheet.m; sourceTree = ""; }; E16D20FA492E085BD0871B107B3077B7 /* DTHTMLWriter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLWriter.m; path = Core/Source/DTHTMLWriter.m; sourceTree = ""; }; E196F56E68578181D5BDE787DE5C0F3A /* DTColor+Compatibility.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "DTColor+Compatibility.m"; path = "Core/Source/DTColor+Compatibility.m"; sourceTree = ""; }; @@ -987,24 +992,22 @@ E20272295EC253826AEE1F83A15D47C5 /* NSAttributedStringRunDelegates.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = NSAttributedStringRunDelegates.h; path = Core/Source/NSAttributedStringRunDelegates.h; sourceTree = ""; }; E27C503C8128BC3CE109FFE077AEF325 /* NSString+CSS.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+CSS.m"; path = "Core/Source/NSString+CSS.m"; sourceTree = ""; }; E28CA95D89DBAB2FE56BC8513D5838D7 /* DTHorizontalRuleHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTHorizontalRuleHTMLElement.h; path = Core/Source/DTHorizontalRuleHTMLElement.h; sourceTree = ""; }; + E295867ADB067E27E538BB6802A15F65 /* RDEPUBReaderHighlightsViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderHighlightsViewController.swift; sourceTree = ""; }; E481007A8174943868EBC81CF3B04B9F /* SSAlertPresentAnimation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SSAlertPresentAnimation.swift; path = Sources/SSAlertSwift/SSAlertPresentAnimation.swift; sourceTree = ""; }; E4F5BD95D13D1554B8DDB8924913442B /* RDEPUBTextLayouter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextLayouter.swift; sourceTree = ""; }; - E500EE11964861814042CF5C8E3EA3A3 /* RDEPUBChapterOffsetMap.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterOffsetMap.swift; sourceTree = ""; }; - E5416F4CC58991D4DFA213ED44B7AE12 /* RDEPUBTextAnnotationOverlay.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextAnnotationOverlay.swift; sourceTree = ""; }; - E58B496CE64A4A768BD528773C58791A /* RDEPUBViewportTypes.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBViewportTypes.swift; sourceTree = ""; }; E5F69A1E1260BAD7737BEB533F643A15 /* DTLazyImageView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTLazyImageView.h; path = Core/Source/DTLazyImageView.h; sourceTree = ""; }; E62C5E593037EF8A45E7FAA847D0277C /* DTCoreTextLayoutFrame+Cursor.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "DTCoreTextLayoutFrame+Cursor.m"; path = "Core/Source/DTCoreTextLayoutFrame+Cursor.m"; sourceTree = ""; }; E676DB9AB5DFC0FB4A33EC63DDACC0E8 /* RDEPUBTextAnchor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextAnchor.swift; sourceTree = ""; }; E6F2E79CC33E0823C4E719ECDDFBBE08 /* NSAttributedStringRunDelegates.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = NSAttributedStringRunDelegates.m; path = Core/Source/NSAttributedStringRunDelegates.m; sourceTree = ""; }; - EAC57BC47A53EE7FA53B0C3CD69FA66D /* RDEPUBReaderDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderDelegate.swift; sourceTree = ""; }; - EB66A78848A8F3845FD918AEA8647871 /* RDEPUBReaderContext.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderContext.swift; sourceTree = ""; }; + E83C08D0923B31898A62D63C06543BCC /* RDURLReaderController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDURLReaderController.swift; sourceTree = ""; }; + EAE8DA169B8772C7191FBA9AB80092F8 /* RDEPUBReaderController+RenderSupport.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBReaderController+RenderSupport.swift"; sourceTree = ""; }; EBA0272559B0F7FAD8AC670AC7431188 /* Pods-ReadViewDemo-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-ReadViewDemo-umbrella.h"; sourceTree = ""; }; EC152E542DB1CE18D5FB239C50E7DD27 /* RDReaderPreloadController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderPreloadController.swift; sourceTree = ""; }; ECFE19508E12BF8B0B52E8CB4F724217 /* ConstraintMakerRelatable+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ConstraintMakerRelatable+Extensions.swift"; path = "Sources/ConstraintMakerRelatable+Extensions.swift"; sourceTree = ""; }; ED5693BD171B879406EA14C97F4AD8D8 /* Archive+ZIP64.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Archive+ZIP64.swift"; path = "Sources/ZIPFoundation/Archive+ZIP64.swift"; sourceTree = ""; }; EDA6D5D7D0A04878EE78FAAE94F65068 /* DTCoreText */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = DTCoreText; path = DTCoreText.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - EE46828BF035DBBC8F92EAF87053BB89 /* RDEPUBChapterWindowSnapshot.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterWindowSnapshot.swift; sourceTree = ""; }; EE4EE750340347ED584C5A2C1164331A /* SSAlertCommonView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SSAlertCommonView.swift; path = Sources/SSAlertSwift/SSAlertCommonView.swift; sourceTree = ""; }; + EE6FE25CC16ECB8AB03FC9995C9074DB /* RDEPUBReaderController+TableOfContents.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RDEPUBReaderController+TableOfContents.swift"; sourceTree = ""; }; EE9D4A455723797A27F04A15C4E47823 /* DTHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTHTMLElement.h; path = Core/Source/DTHTMLElement.h; sourceTree = ""; }; EF27E643619E369E8A9C1FE6EFE18C78 /* DTFoundation.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = DTFoundation.modulemap; sourceTree = ""; }; F038FB491B9A4A53447EF0DC333301AE /* DTObjectTextAttachment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTObjectTextAttachment.m; path = Core/Source/DTObjectTextAttachment.m; sourceTree = ""; }; @@ -1019,19 +1022,18 @@ F5389D4E96A4AFE175691B4D3D4524E7 /* DTExtendedFileAttributes.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTExtendedFileAttributes.m; path = Core/Source/DTExtendedFileAttributes.m; sourceTree = ""; }; F53CCB60DE5D06A43E4A52EDB274E7D2 /* NSString+Paragraphs.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+Paragraphs.m"; path = "Core/Source/NSString+Paragraphs.m"; sourceTree = ""; }; F5B34EE2160C27947D1EEFD42184CF5D /* ConstraintLayoutSupportDSL.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintLayoutSupportDSL.swift; path = Sources/ConstraintLayoutSupportDSL.swift; sourceTree = ""; }; - F66B88725D7C653DDBD5551A375FD47B /* RDEPUBReaderAnnotationCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderAnnotationCoordinator.swift; sourceTree = ""; }; F699881ABDBEB419AEBA661AD5A21F35 /* ZIPFoundation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = ZIPFoundation.release.xcconfig; sourceTree = ""; }; - F6EEF32CBD14BBE7F831F9049D474D53 /* RDEPUBReaderAssemblyCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderAssemblyCoordinator.swift; sourceTree = ""; }; F734214E067BDBDB581911E1A5CDAADB /* DTBreakHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTBreakHTMLElement.m; path = Core/Source/DTBreakHTMLElement.m; sourceTree = ""; }; + F73C1CEF117C3E1E15A1C0F0CE6E34DC /* RDEPUBChapterRuntimeStore.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterRuntimeStore.swift; sourceTree = ""; }; F76A97683FB5E8E8B86EA4AD71F6F4B4 /* Date+ZIP.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Date+ZIP.swift"; path = "Sources/ZIPFoundation/Date+ZIP.swift"; sourceTree = ""; }; F7B0087DE7C394A677F5883541AB4FB7 /* DTListItemHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTListItemHTMLElement.m; path = Core/Source/DTListItemHTMLElement.m; sourceTree = ""; }; F9A2565B302334B662AB51DCF8E65EEE /* DTDictationPlaceholderTextAttachment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTDictationPlaceholderTextAttachment.h; path = Core/Source/DTDictationPlaceholderTextAttachment.h; sourceTree = ""; }; - FAB6B4692FB6CD7D4AF9C67B872DBA82 /* RDEPUBTextPageDecorationView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBTextPageDecorationView.swift; sourceTree = ""; }; FB2ABB1D297442D91AA42F1407DB4A58 /* DTTextHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTTextHTMLElement.h; path = Core/Source/DTTextHTMLElement.h; sourceTree = ""; }; FC5EC7C3D1808C9CF53354D7A3D69EE7 /* NSString+DTPaths.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+DTPaths.h"; path = "Core/Source/NSString+DTPaths.h"; sourceTree = ""; }; + FC629A6C0155AB2BFFCA818A0C89567A /* RDEPUBReaderChromeCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBReaderChromeCoordinator.swift; sourceTree = ""; }; FC645BB4D42BB9762F362DF728F58946 /* RDReaderPageChildViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderPageChildViewController.swift; sourceTree = ""; }; - FC748DACF16D50DAE82F5524E3BC41AB /* RDEPUBChapterDataCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterDataCache.swift; sourceTree = ""; }; FC956678AB4F5B41D2BA3F5F2A2378A7 /* RDEPUBChapterTailNormalizer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterTailNormalizer.swift; sourceTree = ""; }; + FCB085E95929F7729CE5CBC9F17BB712 /* RDEPUBPageResolver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBPageResolver.swift; sourceTree = ""; }; FCC718115E5C31C88A680522AE36B089 /* DTFoundation-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "DTFoundation-dummy.m"; sourceTree = ""; }; FD03567681A79FFB6E81B3105503065D /* DTFoundationConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTFoundationConstants.h; path = Core/Source/DTFoundationConstants.h; sourceTree = ""; }; FDD333B409D528E5006FA48F4D70A6F5 /* DTCSSListStyle.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCSSListStyle.h; path = Core/Source/DTCSSListStyle.h; sourceTree = ""; }; @@ -1043,15 +1045,31 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 340CF789BBC1459D8038BB4A17FEE887 /* Frameworks */ = { + 11428808A2C31AF882AFD4536A862EC8 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F2F9007A3CD34CC638174F8C067583BF /* Foundation.framework in Frameworks */, + 4784055F4929C8C6DD754D3C7862D686 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 529C6D0E8C1835AA081173D34DD20CF0 /* Frameworks */ = { + 11991DA64C95F0209A909294C93238E4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 98F0C9511AA2FA40CD80DC71EB6D4FDF /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 43FB7C3E45EBBADB2DDBA5CF1D4D6526 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2818B9AC60001AB09015CD0BD5C9D339 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 46A46B14E5944D6717708D16DE89649F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( @@ -1066,17 +1084,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 5BE1E93AF8088318650FAF10BFBB1959 /* Frameworks */ = { + 5D9782EE854A9BBDB34F0D07F07A11EB /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 5FEF2AEADB43680099867F045E9CCEB0 /* Frameworks */ = { + 66031DEDB2E454284283E86939650723 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 593CA36C8608F7B33BEFB64959449B76 /* Foundation.framework in Frameworks */, + 139CC4381EDEA18EA5EA6FC4D3E62193 /* ImageIO.framework in Frameworks */, + 9BA35FC883C9B3C63EE3D0BFEC53689A /* QuartzCore.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1093,41 +1114,22 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 974AB592D855798D3E95942DF6A2836E /* Frameworks */ = { + 71F8616A075DBC57C8152A9C0B48AC1E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - FDC76C5EFA6E71299BCC5F4BD028FD3D /* Foundation.framework in Frameworks */, + F9C877C6095F38943FBB625B32FB23A5 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - ADCC589032705B21C463F77C291FE439 /* Frameworks */ = { + 7A387A49D1418E18C0719DD3BE91ACBB /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 55F9FC052EC79F1184E3D8108AEF2648 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - BAAA0ADC1A1BB29F4536DEF80F9DC8CF /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 98817BF0F2B027A249DB89AE80C178E8 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - CDDEEFBFB7C6A4EB06E1C7669FE0459A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 2550FB3DC06D75BF7265CD7924DC1807 /* Foundation.framework in Frameworks */, - 3AAF769EB8C7417ADBF6CFC3D622B2FE /* ImageIO.framework in Frameworks */, - BA3D4212AC53469F96C5F4A5770491B2 /* QuartzCore.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - DAFEA3A215B4144C06904B9ED0559500 /* Frameworks */ = { + CA9D9F3471E999EF80B22BFCB4D52D46 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( @@ -1250,22 +1252,6 @@ path = "../Target Support Files/DTFoundation"; sourceTree = ""; }; - 29799540433208DC6B7C651E19F2900E /* TextPage */ = { - isa = PBXGroup; - children = ( - 356DE2A2E8D844E60004E3AAB7B524FA /* RDEPUBPageInteractionController.swift */, - 5C7E7F66A26C2FC81CA8C11585A91C6C /* RDEPUBPageLayoutSnapshot.swift */, - 1DF55EE5FC41651D54FEAD1F8097F034 /* RDEPUBSelectionOverlayView.swift */, - E5416F4CC58991D4DFA213ED44B7AE12 /* RDEPUBTextAnnotationOverlay.swift */, - 266AEC0C2D6983818D5C107DEA5BB286 /* RDEPUBTextContentView.swift */, - FAB6B4692FB6CD7D4AF9C67B872DBA82 /* RDEPUBTextPageDecorationView.swift */, - 7516CFAD05F8C77E5C44B21BAA4E0FD2 /* RDEPUBTextPageRenderView.swift */, - D96987335475BD8BA2521AFC376AA33C /* RDEPUBTextSelectionController.swift */, - ); - name = TextPage; - path = TextPage; - sourceTree = ""; - }; 29A410FBD4C19138B1D732EC94D8EFB2 /* DTFoundation */ = { isa = PBXGroup; children = ( @@ -1304,6 +1290,18 @@ name = DTAnimatedGIF; sourceTree = ""; }; + 3C5366D72420E40F3833DE5727D9D02D /* Settings */ = { + isa = PBXGroup; + children = ( + 1E7CF983E6CF4797FA35E3B583ECA941 /* RDEPUBReaderConfiguration.swift */, + 9A0C8E542EFB0872BF4471EB2EDB06EA /* RDEPUBReaderSettings.swift */, + 7AD6EF544C3C1F037842DF5E4CD048B0 /* RDEPUBReaderSettingsViewController.swift */, + 6B1E75CAB3F7C45A794F6C5335FFECBB /* RDEPUBReaderTheme.swift */, + ); + name = Settings; + path = Settings; + sourceTree = ""; + }; 43D8ECA62CF48C3CD5F77E3CA6F466AF /* Products */ = { isa = PBXGroup; children = ( @@ -1322,6 +1320,30 @@ name = Products; sourceTree = ""; }; + 48E8EF2D06B474007516336DE226CB5B /* ChapterRuntime */ = { + isa = PBXGroup; + children = ( + 5B6A71ED4237A07E81D0597E56658C5A /* RDEPUBBackgroundTrace.swift */, + D4D1E00E7B5F60E25B326CF4B83E22B2 /* RDEPUBBookPageMap.swift */, + 91EAE48AF0DC04CF719A938AD5613EC3 /* RDEPUBChapterCacheKey.swift */, + 3068218D3A08F227DDA6216F94FB69F4 /* RDEPUBChapterDataCache.swift */, + BB8687FD6159F07520A97D89A4A739AB /* RDEPUBChapterLoader.swift */, + D7BBD50992F22841D962895AED7442C7 /* RDEPUBChapterLocation.swift */, + AE57AC7D620527A77DEF6EB1162C9DFC /* RDEPUBChapterOffsetMap.swift */, + F73C1CEF117C3E1E15A1C0F0CE6E34DC /* RDEPUBChapterRuntimeStore.swift */, + D2C33E5B1677A732781A827A0A58805F /* RDEPUBChapterSummaryDiskCache.swift */, + B02BB9570D018CF824C0024AEDA9DF92 /* RDEPUBChapterWindowCoordinator.swift */, + 3C56140C935C7B3F792647614D702DAB /* RDEPUBChapterWindowSnapshot.swift */, + 0B2963687ADF8375028F8C6F50B1F40B /* RDEPUBPageCountCache.swift */, + FCB085E95929F7729CE5CBC9F17BB712 /* RDEPUBPageResolver.swift */, + B8CC24C31135ADAF3F1271D5BA167262 /* RDEPUBRuntimeChapter.swift */, + D21EFC0B1DC22A993EB8B62715E87FC0 /* RDEPUBRuntimePageCount.swift */, + 5B03892CC4EE09B2E429D0F06E2296BD /* String+SHA256.swift */, + ); + name = ChapterRuntime; + path = ChapterRuntime; + sourceTree = ""; + }; 49882ECBC24E71ACA5E18D6AE9EA639D /* SSAlertSwift */ = { isa = PBXGroup; children = ( @@ -1447,6 +1469,28 @@ name = Pod; sourceTree = ""; }; + 7D263A04A2FE2BEAAB3A8A6CC0A42974 /* ReaderController */ = { + isa = PBXGroup; + children = ( + 88DC78A6C33BEF2BC4ADB375F162C284 /* RDEPUBReaderAnnotationCoordinator.swift */, + CA3ACBCB65E911E12245EDA437DF3088 /* RDEPUBReaderAssemblyCoordinator.swift */, + FC629A6C0155AB2BFFCA818A0C89567A /* RDEPUBReaderChromeCoordinator.swift */, + 12109749AFDE57CACC5E56AB63AD3EB2 /* RDEPUBReaderContext.swift */, + 6B3C0665F2995A9C45FCAABC3F62DE3D /* RDEPUBReaderDependencies.swift */, + CD0D4092A12656EBCF1A6F72B4FD4082 /* RDEPUBReaderLoadCoordinator.swift */, + 4D02D5C9FBB27E031A6D934A07FB001F /* RDEPUBReaderLocationCoordinator.swift */, + E088FB8503A3AEBBB051817E55410CAE /* RDEPUBReaderPaginationCoordinator.swift */, + 94798D2C9B825493840EF99A7447AFB7 /* RDEPUBReaderRuntime.swift */, + 2451995F95C8B333F972A39BDFC7EBCE /* RDEPUBReaderSearchCoordinator.swift */, + 43C106392FA85206E0EE3E17BDDEFF91 /* RDEPUBReaderUIState.swift */, + 8DE6D61DD1D70A6DDF12430979C4082A /* RDEPUBReaderViewportMonitor.swift */, + BF725A63AD2B9E43F33EB99F07C304D4 /* RDEPUBSelectionState.swift */, + 48E8EF2D06B474007516336DE226CB5B /* ChapterRuntime */, + ); + name = ReaderController; + path = ReaderController; + sourceTree = ""; + }; 81FAF4124052D4A411C0A8C21B48174C /* Core */ = { isa = PBXGroup; children = ( @@ -1454,31 +1498,6 @@ name = Core; sourceTree = ""; }; - 8A99CE355DD2AD213A11505522A649DF /* ChapterRuntime */ = { - isa = PBXGroup; - children = ( - 6ABEFE84724594A5A9040F3C67BAFD3B /* RDEPUBBackgroundTrace.swift */, - C1E1631C6F8CBDBBDDC1DC595AF83F4E /* RDEPUBBookPageMap.swift */, - CC7081E8A3F5A8A212D8F11DA4582929 /* RDEPUBChapterCacheKey.swift */, - FC748DACF16D50DAE82F5524E3BC41AB /* RDEPUBChapterDataCache.swift */, - 3DDB56AC5412CF16FDBB8BF9A606E9F1 /* RDEPUBChapterLoader.swift */, - B9EF15D29CE2E35CD8D3439E3C91EEC9 /* RDEPUBChapterLocation.swift */, - E500EE11964861814042CF5C8E3EA3A3 /* RDEPUBChapterOffsetMap.swift */, - 2335B9D3BB17D5D8C1AE0AF4D9365BD4 /* RDEPUBChapterRuntimeStore.swift */, - 2F3C3B060FF4C0BF31718EDE2DB45BB9 /* RDEPUBChapterSummaryDiskCache.swift */, - 0463606FFE0A076D81FC1ECDE5C47259 /* RDEPUBChapterWindowCoordinator.swift */, - EE46828BF035DBBC8F92EAF87053BB89 /* RDEPUBChapterWindowSnapshot.swift */, - C9F07C5EDFE63EB17570FE17E8E6E1B8 /* RDEPUBLocationConverter.swift */, - 40B0AA875D62D53E865F10DC10DB4A4E /* RDEPUBPageCountCache.swift */, - A5192CA781CBC54A3FDB730E48E1F25A /* RDEPUBPageResolver.swift */, - 05DFBF4E36FCC77E8BE2B51DF47B7F33 /* RDEPUBRuntimeChapter.swift */, - 3785B10F33122FC204F180B9E2366069 /* RDEPUBRuntimePageCount.swift */, - D5F6D9DC5EAEE10F7D8644F06CEE3C99 /* String+SHA256.swift */, - ); - name = ChapterRuntime; - path = ChapterRuntime; - sourceTree = ""; - }; 8E502710D4B9C832D749FEDB495778AC /* RDReaderView */ = { isa = PBXGroup; children = ( @@ -1494,7 +1513,7 @@ 314D77A91FF74A0ACD762C92A6C1AF19 /* wxread-replace-latin.css */, B805DA3735FAD2A62B0C314361B07780 /* EPUBCore */, D19910C1E9AB1D38B8D16F5EC00F66E0 /* EPUBTextRendering */, - EA1FE81FEF1BEFCC3C3C092F5AD343DD /* EPUBUI */, + 91A99FC917F9940FB2874BB13A90E753 /* EPUBUI */, 7962DFBE557F520D3FCBD088453DEF88 /* Pod */, EDF2681E1CA5AEE3C96023483165563C /* ReaderView */, 591EE9495516A39AB50A3C67F70A5FB8 /* Support Files */, @@ -1503,16 +1522,52 @@ path = ../..; sourceTree = ""; }; - 97FF07743BDCB7F7F4A94A49D0CB53CF /* Settings */ = { + 91A99FC917F9940FB2874BB13A90E753 /* EPUBUI */ = { isa = PBXGroup; children = ( - 9B81846EA13E575E1459B41F96ACB244 /* RDEPUBReaderConfiguration.swift */, - 591427AC1FAE583EB7EAC76B1E673CDA /* RDEPUBReaderSettings.swift */, - 6714862E1752548842941DFE880513C0 /* RDEPUBReaderSettingsViewController.swift */, - 4E2687A6F390F43603F456693C4D2D90 /* RDEPUBReaderTheme.swift */, + 61CAA3B6C9BC1F1E4C35A108D76B1E79 /* RDEPUBReaderBottomToolView.swift */, + 5CA26E2111C84E605445082FAD421636 /* RDEPUBReaderChapterListController.swift */, + 20E5F9A5250EB5724D516BF67152CBB8 /* RDEPUBReaderController.swift */, + 3562A5E853CB707C1AC4E74C3A8E3924 /* RDEPUBReaderController+ContentDelegates.swift */, + B04B186DAA39C121040AC3B1018D12E8 /* RDEPUBReaderController+DataSource.swift */, + 26C542F1825BDD5743D52350B8F4788D /* RDEPUBReaderController+PublicAPI.swift */, + EAE8DA169B8772C7191FBA9AB80092F8 /* RDEPUBReaderController+RenderSupport.swift */, + 0813D235A2C7FFD9AC3F254EFC9F5FFC /* RDEPUBReaderController+RuntimeBridge.swift */, + EE6FE25CC16ECB8AB03FC9995C9074DB /* RDEPUBReaderController+TableOfContents.swift */, + D7A3D6CCECEDECBA9575590F2349E274 /* RDEPUBReaderDelegate.swift */, + E295867ADB067E27E538BB6802A15F65 /* RDEPUBReaderHighlightsViewController.swift */, + 60F861867F08812A37E8DDCBB02DC583 /* RDEPUBReaderPersistence.swift */, + AE6AED7CB30C4F5DF029315D9602ABE2 /* RDEPUBReaderSearchBarView.swift */, + 902D8268D209ABBB2768E10905B189C2 /* RDEPUBReaderTableOfContentsItem.swift */, + 16A1F97BF3EA1335405B877D02898D34 /* RDEPUBReaderToolView.swift */, + 9255AF737FE4AA3AE50EE4F2BDCC9AD7 /* RDEPUBReaderTopToolView.swift */, + 9899192BA37086232FFEA93F5B2EC3B3 /* RDEPUBViewportTypes.swift */, + 32E5D40E2A0541080719F92638244887 /* RDEPUBWebContentView.swift */, + CFBCC74BCB4330EB8EF1B57BADBB014B /* RDEPUBWebDecorationOverlayView.swift */, + E83C08D0923B31898A62D63C06543BCC /* RDURLReaderController.swift */, + 1DF05A515E551E5511DBE86C4A494EF1 /* UIColor+RDEPUBHex.swift */, + 7D263A04A2FE2BEAAB3A8A6CC0A42974 /* ReaderController */, + 3C5366D72420E40F3833DE5727D9D02D /* Settings */, + 975211ADBAB4007AD0DC73C3FD8D1133 /* TextPage */, ); - name = Settings; - path = Settings; + name = EPUBUI; + path = Sources/RDReaderView/EPUBUI; + sourceTree = ""; + }; + 975211ADBAB4007AD0DC73C3FD8D1133 /* TextPage */ = { + isa = PBXGroup; + children = ( + 012E2E783F3E83E70D3A5CE2DD8DE007 /* RDEPUBPageInteractionController.swift */, + 357CF3DD6846FD95FE05679E6ED3866F /* RDEPUBPageLayoutSnapshot.swift */, + 51A0D2BE0587C3DDC57DDBDCD73F81BD /* RDEPUBSelectionOverlayView.swift */, + CBF22F617BFAC4143D2661C887ACACDB /* RDEPUBTextAnnotationOverlay.swift */, + BC30482E1E56725E225659BE42ED79EC /* RDEPUBTextContentView.swift */, + 600151C0D6D719C385CC4D3FCA6E0B27 /* RDEPUBTextPageDecorationView.swift */, + 3F2782574376AE28B6377B05472691E6 /* RDEPUBTextPageRenderView.swift */, + 2909100BDF5A0331D6BE22AF122BB95F /* RDEPUBTextSelectionController.swift */, + ); + name = TextPage; + path = TextPage; sourceTree = ""; }; 994C39A644C0722C7A9E97AB46DB8965 /* Targets Support Files */ = { @@ -1770,26 +1825,6 @@ name = UIKit; sourceTree = ""; }; - CD2BA32D56765AE543578FA700C0C830 /* ReaderController */ = { - isa = PBXGroup; - children = ( - F66B88725D7C653DDBD5551A375FD47B /* RDEPUBReaderAnnotationCoordinator.swift */, - F6EEF32CBD14BBE7F831F9049D474D53 /* RDEPUBReaderAssemblyCoordinator.swift */, - 10AF8170B881EC5CFA3C1CA4AC3E27F1 /* RDEPUBReaderChromeCoordinator.swift */, - EB66A78848A8F3845FD918AEA8647871 /* RDEPUBReaderContext.swift */, - 290EA639B2AE8B8092D7C08A581CD970 /* RDEPUBReaderDependencies.swift */, - 6567762A90252DF61DBD13E3CCB5BF52 /* RDEPUBReaderLoadCoordinator.swift */, - 1D0DCF841D23F8526D19767D66A932DC /* RDEPUBReaderLocationCoordinator.swift */, - D6E097E98410554175AFEAF5BF4CC963 /* RDEPUBReaderPaginationCoordinator.swift */, - DE5CC526B804F5385241DD0D5A71831E /* RDEPUBReaderRuntime.swift */, - 89F87AEC289B931D4CA74AD9BACE1736 /* RDEPUBReaderSearchCoordinator.swift */, - 8F01C26E4C66501E3CEB74C752FA77D0 /* RDEPUBReaderViewportMonitor.swift */, - 8A99CE355DD2AD213A11505522A649DF /* ChapterRuntime */, - ); - name = ReaderController; - path = ReaderController; - sourceTree = ""; - }; CF1408CF629C7361332E53B88F7BD30C = { isa = PBXGroup; children = ( @@ -1912,38 +1947,6 @@ name = Pods; sourceTree = ""; }; - EA1FE81FEF1BEFCC3C3C092F5AD343DD /* EPUBUI */ = { - isa = PBXGroup; - children = ( - 778702DC79D5849F3C4DF790310D0917 /* RDEPUBReaderBottomToolView.swift */, - D74BFC8E9133BF4E551E3A93798B4F99 /* RDEPUBReaderChapterListController.swift */, - 7CBD2948DE6941D36BAF37A94CC7981A /* RDEPUBReaderController.swift */, - 5DC3AB46FAF0445813FD5AFA0E66194C /* RDEPUBReaderController+ContentDelegates.swift */, - 0F02953DC627CB32FFD38E101007BB9E /* RDEPUBReaderController+DataSource.swift */, - 7BB3C8634528BC5B2BC8EA70F1FD14FE /* RDEPUBReaderController+PublicAPI.swift */, - C0281BCEC7C2071D2996CBAC72E78DC2 /* RDEPUBReaderController+RenderSupport.swift */, - 7F278454696A046C7789CF994A17CE53 /* RDEPUBReaderController+RuntimeBridge.swift */, - 1D44639116A69F6C16EC09917C13DD18 /* RDEPUBReaderController+TableOfContents.swift */, - EAC57BC47A53EE7FA53B0C3CD69FA66D /* RDEPUBReaderDelegate.swift */, - 41AF383869619DE580A94B69EAC6ECF5 /* RDEPUBReaderHighlightsViewController.swift */, - 68C5ACCC99754FDAFCC9F78F9163AC5E /* RDEPUBReaderPersistence.swift */, - 2BCA80CA6D8AE27898A0F352AA19FB31 /* RDEPUBReaderSearchBarView.swift */, - 3636FB4CE567BE823BB3A4326A466986 /* RDEPUBReaderTableOfContentsItem.swift */, - 5921C735CE80E2A8BB3B05A1914CCC16 /* RDEPUBReaderToolView.swift */, - D8D9882EB39FEC92F33CDC352AF14F3E /* RDEPUBReaderTopToolView.swift */, - E58B496CE64A4A768BD528773C58791A /* RDEPUBViewportTypes.swift */, - 2CAFA445DFFA6C8B61C7306D968C7595 /* RDEPUBWebContentView.swift */, - 81EAF121C946325DF0FA84458CDFA926 /* RDEPUBWebDecorationOverlayView.swift */, - D4FF37961C00CE5162EE9165F6EC5E1A /* RDURLReaderController.swift */, - C68BF07B565C3B6919A14BD1973D2AEE /* UIColor+RDEPUBHex.swift */, - CD2BA32D56765AE543578FA700C0C830 /* ReaderController */, - 97FF07743BDCB7F7F4A94A49D0CB53CF /* Settings */, - 29799540433208DC6B7C651E19F2900E /* TextPage */, - ); - name = EPUBUI; - path = Sources/RDReaderView/EPUBUI; - sourceTree = ""; - }; EDF2681E1CA5AEE3C96023483165563C /* ReaderView */ = { isa = PBXGroup; children = ( @@ -2055,68 +2058,68 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2CC5E6F0F49DE2AC6D348CE3DA65A63F /* Headers */ = { + 2BC178966B74414BEE513D3EE9A97F61 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 5DA72BD45B149DB685C7991AB6BC217B /* DTActivityTitleView.h in Headers */, - 095388DD719D03060DF1BBD554BD2FEF /* DTAnimatedGIF.h in Headers */, - 4741B2B966F91956089CADFF6C1D8F21 /* DTBase64Coding.h in Headers */, - 92C6605AA2E946069E33F146C95E92CB /* DTBlockFunctions.h in Headers */, - 893152C549C2C7FBD1D4B0AD192A995E /* DTCompatibility.h in Headers */, - F002E4B5F2AA5C5814904D907E424E04 /* DTCoreGraphicsUtils.h in Headers */, - 1E90149260990A88EBA1EBDE5C03EA9C /* DTCustomColoredAccessory.h in Headers */, - DB96436F5A79D5AD419C48D9283608E9 /* DTExtendedFileAttributes.h in Headers */, - F8314CC437C8756EDE5CE69D6011AAB6 /* DTFolderMonitor.h in Headers */, - 5F6EDC70A6A29FD65F297DA272CEB1FD /* DTFoundation-umbrella.h in Headers */, - F1068326AAA87C25D4C0BD09FF1DB537 /* DTFoundationConstants.h in Headers */, - 845A6C31B9A7E4E57C81CB12A6768494 /* DTHTMLParser.h in Headers */, - BC66D1AB07E873288D6D0FCEB06B29FE /* DTLog.h in Headers */, - AA303B108A49B03A4DE75B195BEFBE75 /* DTPieProgressIndicator.h in Headers */, - B8420EA8242E549CC4A2068633959D02 /* DTSmartPagingScrollView.h in Headers */, - 298DB0F0F244610847C472F016BF2676 /* DTTiledLayerWithoutFade.h in Headers */, - FB65E0C6E2E9AB5FCDAFC6090BEF2CFE /* DTVersion.h in Headers */, - 0393E3298DC788FD7C328C2CBB1ADD71 /* DTWeakSupport.h in Headers */, - 0BFB92A40ED6F693E9985B94C16BBA06 /* NSArray+DTError.h in Headers */, - 06A622DE7D129677B3A306A2D542DA86 /* NSData+DTCrypto.h in Headers */, - FD30AEA440323EDF5AD0C41402570F01 /* NSDictionary+DTError.h in Headers */, - 5DFA66DC2F6B8ACE4D9C01A8DC268196 /* NSFileWrapper+DTCopying.h in Headers */, - 4847AAA31E7027FA8A0CEF293EDFB284 /* NSMutableArray+DTMoving.h in Headers */, - 8105CDF3B8B8A7B69EEF5C92718C00E6 /* NSString+DTFormatNumbers.h in Headers */, - 0F1682ED32DDE83824C396E904963AA5 /* NSString+DTPaths.h in Headers */, - 7FBE67FC4DE9151B3E9529926FBE55BA /* NSString+DTURLEncoding.h in Headers */, - D699A38F72CF8A51F0A3E5A6BF91259D /* NSString+DTUtilities.h in Headers */, - 769A0500BBDD01FAFD03C168C295D835 /* NSURL+DTAppLinks.h in Headers */, - 1EAC8D7FE1D8B98841823C8F8E6BF856 /* NSURL+DTComparing.h in Headers */, - 92966B2B3DBC8A298D31A0F2EC7453B7 /* NSURL+DTUnshorten.h in Headers */, - 2BB6A5DF21E5E8AB6E5793A189746DC9 /* UIApplication+DTNetworkActivity.h in Headers */, - 0D6C585E17F4576329E50CF07B85CE15 /* UIImage+DTFoundation.h in Headers */, - 1733809C4DE2D2268981BB9421338594 /* UIScreen+DTFoundation.h in Headers */, - E95293260204B17F0A630D8E6F6CAA84 /* UIView+DTFoundation.h in Headers */, + AAC840547DD17D5C75BE4893AEC920D1 /* SnapKit-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; - 62EC6FDA84F047BC75CB09A4E2D18BC1 /* Headers */ = { + 4B2B66996212DC2632DB4C3DFB61EB18 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - D0EC11725D1CA8C1CDDB18BC6BD93ADF /* ZIPFoundation-umbrella.h in Headers */, + E88BE97E9C863275B42FAE9A893B011E /* RDReaderView-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; - 6E3CDDA2380B09BE19DD021C4E4A2C88 /* Headers */ = { + 5FE226E86427545F25E04DF4CAD85840 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 274577F301594F7C79997B43A939DEC9 /* SnapKit-umbrella.h in Headers */, + E1635E1C51B4232C2868F80F501E2CCB /* DTActivityTitleView.h in Headers */, + BBA44069DE29E393B2A3CE8DD7DF6552 /* DTAnimatedGIF.h in Headers */, + 0E3D5C67966CB30F8A1C5B23B32A78C6 /* DTBase64Coding.h in Headers */, + 25D2035E54373625D7DDE374536A1A22 /* DTBlockFunctions.h in Headers */, + A023C6257ACFCB4BB2C3A0A780D3BA69 /* DTCompatibility.h in Headers */, + DF611CA46A11935B195229A13F367FE3 /* DTCoreGraphicsUtils.h in Headers */, + A0CA0D794B98DDD54311F0784A7214C5 /* DTCustomColoredAccessory.h in Headers */, + B91AF32D50A82B44DDAFD1D77382AB2D /* DTExtendedFileAttributes.h in Headers */, + 0724227458D6158434EB3BA34EF75208 /* DTFolderMonitor.h in Headers */, + 9C3540F5054EF5CC5D8B122C39934B17 /* DTFoundation-umbrella.h in Headers */, + ABEC4B76C9F6444D84E18C262ACCF327 /* DTFoundationConstants.h in Headers */, + A1EE5A08AF16702B7DF8D3881B1DA9C1 /* DTHTMLParser.h in Headers */, + CC3A3AC5FDD073E23A4FB9751E1906F6 /* DTLog.h in Headers */, + 0348E85E071F1963286B90D704A584EB /* DTPieProgressIndicator.h in Headers */, + 86A1119C520665097B6028AFE68FB9F6 /* DTSmartPagingScrollView.h in Headers */, + AEFE45633FE7519934D64924E411B57E /* DTTiledLayerWithoutFade.h in Headers */, + B75282EB6F6EA0AD668AECD3C83F66BD /* DTVersion.h in Headers */, + 1646DB61088E6EA55035448A0D00F9EC /* DTWeakSupport.h in Headers */, + 9361F0CAA37A3D0F59158DA361B3403B /* NSArray+DTError.h in Headers */, + 9705BC91D9FE89A08A6794D92FC852E6 /* NSData+DTCrypto.h in Headers */, + B2418E155320F3D2B933ACC133B1E946 /* NSDictionary+DTError.h in Headers */, + 1755FA3D3F51158F1178A2788CBD2E3E /* NSFileWrapper+DTCopying.h in Headers */, + 871BED7D64905AA927C5EC750B564494 /* NSMutableArray+DTMoving.h in Headers */, + E215C744B7C0F0F1468F0B1BA37A5A26 /* NSString+DTFormatNumbers.h in Headers */, + 437E6DEFBB36398B5CF02CEF30E5AB96 /* NSString+DTPaths.h in Headers */, + 4E30F0CCCE719F18008FBA8F0A50FBEA /* NSString+DTURLEncoding.h in Headers */, + A43A5EA6C160F881A43859992FDEC335 /* NSString+DTUtilities.h in Headers */, + 961088C5DEA62E762924A9E4158B7931 /* NSURL+DTAppLinks.h in Headers */, + 15BB854C628E532A7F08FEA5594F3933 /* NSURL+DTComparing.h in Headers */, + 0D711A72DDF98852C2B1B0845AEB2E51 /* NSURL+DTUnshorten.h in Headers */, + 616A164035FC6A7F5FD132B83DE46596 /* UIApplication+DTNetworkActivity.h in Headers */, + 67975174935DF5A3D66C101151239252 /* UIImage+DTFoundation.h in Headers */, + E80CE3276D704909789FE7AFC3AD71C3 /* UIScreen+DTFoundation.h in Headers */, + 67AE6029E724432408917C8A69CC4589 /* UIView+DTFoundation.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; - 94D77AFA09F75B8E22FC06D6E0F38A51 /* Headers */ = { + A59A9012F49137C2610187ED7A721817 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 53FD0BA15BAA579860ADB13511BB470A /* RDReaderView-umbrella.h in Headers */, + 075B6A431606735FF10DCA4BA1599B61 /* SSAlertSwift-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2128,11 +2131,11 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - CB970AE212B6C4FDDC8C317E938F2992 /* Headers */ = { + E6D0088EEBD5A74ABF246AF7C4535D38 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - F4CBA7CAF93F2E7CDA1F48103F1E2196 /* SSAlertSwift-umbrella.h in Headers */, + AD17641501D68E8C21FAEC49743E4106 /* ZIPFoundation-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2141,11 +2144,11 @@ /* Begin PBXNativeTarget section */ 0C24CB0E87A728A11AA1124CB360D6A1 /* DTCoreText-Resources */ = { isa = PBXNativeTarget; - buildConfigurationList = 2DAE2EE6565CA746E723D0C94409A244 /* Build configuration list for PBXNativeTarget "DTCoreText-Resources" */; + buildConfigurationList = A7E7C52E8356B363E4C668C76D72C91E /* Build configuration list for PBXNativeTarget "DTCoreText-Resources" */; buildPhases = ( - 4F6B6367A29D714DE3A990196B6E3A5A /* Sources */, - DAFEA3A215B4144C06904B9ED0559500 /* Frameworks */, - D4E4F8471951F5B95CADF742EF6FABC7 /* Resources */, + D14625340E2861C36773F9C4484DFD0C /* Sources */, + CA9D9F3471E999EF80B22BFCB4D52D46 /* Frameworks */, + 1AF264E47FED0EDDF12198A018A67A89 /* Resources */, ); buildRules = ( ); @@ -2158,17 +2161,17 @@ }; 19622742EBA51E823D6DAE3F8CDBFAD4 /* SnapKit */ = { isa = PBXNativeTarget; - buildConfigurationList = 4A07C786C7B54AD03891D3FF6D6C3BED /* Build configuration list for PBXNativeTarget "SnapKit" */; + buildConfigurationList = 5165AACF46A63B4FB8A8420ACC759DD3 /* Build configuration list for PBXNativeTarget "SnapKit" */; buildPhases = ( - 6E3CDDA2380B09BE19DD021C4E4A2C88 /* Headers */, - F41891DC36E4B8E47C081F0DB953978A /* Sources */, - 340CF789BBC1459D8038BB4A17FEE887 /* Frameworks */, - 712E8B02D634542EF896E9CF60FF0E43 /* Resources */, + 2BC178966B74414BEE513D3EE9A97F61 /* Headers */, + 2569210201D48992A7ECE5A8C49D82FC /* Sources */, + 71F8616A075DBC57C8152A9C0B48AC1E /* Frameworks */, + AF7C21FCC360BAF5B3E2C82C7C06EB82 /* Resources */, ); buildRules = ( ); dependencies = ( - 777A2E94D64DA72CF95884726BC24EEA /* PBXTargetDependency */, + 77D3AA74C59602B92A392CFC63E44F0F /* PBXTargetDependency */, ); name = SnapKit; productName = SnapKit; @@ -2177,12 +2180,12 @@ }; 8619D5ADECF2B26CFD9A9826D61D289A /* SSAlertSwift */ = { isa = PBXNativeTarget; - buildConfigurationList = 99AFD3DA8C698FBE12BF062246D560F5 /* Build configuration list for PBXNativeTarget "SSAlertSwift" */; + buildConfigurationList = A8B16500C77E14435BD81E2FC3C99BCD /* Build configuration list for PBXNativeTarget "SSAlertSwift" */; buildPhases = ( - CB970AE212B6C4FDDC8C317E938F2992 /* Headers */, - 9B88ED6B8C1D60DE29790A2CBAEE55A8 /* Sources */, - 974AB592D855798D3E95942DF6A2836E /* Frameworks */, - F321BEF43E22E987ECB804D91AAD23FA /* Resources */, + A59A9012F49137C2610187ED7A721817 /* Headers */, + 98DB2E7FF91A6985191703CBEB926865 /* Sources */, + 11991DA64C95F0209A909294C93238E4 /* Frameworks */, + 0AD565F994D903CCC0BA1301D0C72157 /* Resources */, ); buildRules = ( ); @@ -2205,12 +2208,12 @@ buildRules = ( ); dependencies = ( - 73CF3460790CE3E276D2BF53928EACFF /* PBXTargetDependency */, - 56B1FF14CF990DE2239DEC252C19E5F8 /* PBXTargetDependency */, - DD6B5CE86D7AD6AF30BA7C3C7FE438BA /* PBXTargetDependency */, - 645796323263BF71ED05ED05D44F93CD /* PBXTargetDependency */, - 26D085C3E077507E2B80DC3E912CFE56 /* PBXTargetDependency */, - A35B91F3BDE34ED4C32DF403989B4C30 /* PBXTargetDependency */, + 13585C5584DA3266D4A24DDD9B31AD55 /* PBXTargetDependency */, + 2E712D8870E5EBCA7A222177E8449AC1 /* PBXTargetDependency */, + 7700CDB869DDB5BC16AA1C5083D98C45 /* PBXTargetDependency */, + 197FB2078B81A644D08043561984F27E /* PBXTargetDependency */, + CAB448101F2C74E72C200BE40545771B /* PBXTargetDependency */, + 52F662FBC328D9ABFA736CA337E4A2F7 /* PBXTargetDependency */, ); name = "Pods-ReadViewDemo"; productName = Pods_ReadViewDemo; @@ -2219,11 +2222,11 @@ }; 8A8DB685241263AFDF5E6B20FE67B93A /* SnapKit-SnapKit_Privacy */ = { isa = PBXNativeTarget; - buildConfigurationList = 36824C5E16CF085752ED979AE1202BE2 /* Build configuration list for PBXNativeTarget "SnapKit-SnapKit_Privacy" */; + buildConfigurationList = 490DB4724C304AF051ED35D068A20B47 /* Build configuration list for PBXNativeTarget "SnapKit-SnapKit_Privacy" */; buildPhases = ( - BA36A5FE12DEB6B1C60C9352E1BB07DA /* Sources */, - 5BE1E93AF8088318650FAF10BFBB1959 /* Frameworks */, - 43FC2D4C1D214D03D882BD2AF7942FBC /* Resources */, + B2F045F25878E43023D094161DD9F5B6 /* Sources */, + 7A387A49D1418E18C0719DD3BE91ACBB /* Frameworks */, + FB83DEA07B2DEDEA330961EF51164943 /* Resources */, ); buildRules = ( ); @@ -2236,12 +2239,12 @@ }; 8F6E5A5BF72D62CDFD25F91A7CFA3309 /* DTFoundation */ = { isa = PBXNativeTarget; - buildConfigurationList = DBBE36D4A67C028942B51F5CFD5154ED /* Build configuration list for PBXNativeTarget "DTFoundation" */; + buildConfigurationList = 2A92134767212E0AF1CE27B20F859A21 /* Build configuration list for PBXNativeTarget "DTFoundation" */; buildPhases = ( - 2CC5E6F0F49DE2AC6D348CE3DA65A63F /* Headers */, - FF9D1B44C98BC984EAA82543F0F12BD4 /* Sources */, - CDDEEFBFB7C6A4EB06E1C7669FE0459A /* Frameworks */, - B84E9DC361EC46C16A8C9ED9108D1066 /* Resources */, + 5FE226E86427545F25E04DF4CAD85840 /* Headers */, + B81ADB90CEB0C2BC5C3A5EB71B740B22 /* Sources */, + 66031DEDB2E454284283E86939650723 /* Frameworks */, + 3B9155516824ED6A3FA4D004AEE64E62 /* Resources */, ); buildRules = ( ); @@ -2254,17 +2257,17 @@ }; AA15C8469D67684160CC2A7098EB841C /* ZIPFoundation */ = { isa = PBXNativeTarget; - buildConfigurationList = 975641A0350D40E81E27E9F94D6367E1 /* Build configuration list for PBXNativeTarget "ZIPFoundation" */; + buildConfigurationList = AA6CCE7B84211FC7AD7D89714B12CE1E /* Build configuration list for PBXNativeTarget "ZIPFoundation" */; buildPhases = ( - 62EC6FDA84F047BC75CB09A4E2D18BC1 /* Headers */, - 407323155E3E9FFEFF26E57CE4E37F74 /* Sources */, - ADCC589032705B21C463F77C291FE439 /* Frameworks */, - E72FA96C85146FF56A1B6704BE34A344 /* Resources */, + E6D0088EEBD5A74ABF246AF7C4535D38 /* Headers */, + 404DE8BB8ADE877FE966E92F9757E95C /* Sources */, + 43FB7C3E45EBBADB2DDBA5CF1D4D6526 /* Frameworks */, + BF9E38AF3120453D38670CA7B4B83D37 /* Resources */, ); buildRules = ( ); dependencies = ( - 8082ADB56D3087F46C2C3AE8276C048E /* PBXTargetDependency */, + 7128F364EDA5D95565A3FF0D037494D5 /* PBXTargetDependency */, ); name = ZIPFoundation; productName = ZIPFoundation; @@ -2273,21 +2276,21 @@ }; AA2E57587AA8EECA63C4BE08EA3CB6D2 /* RDReaderView */ = { isa = PBXNativeTarget; - buildConfigurationList = 29BF4529A766777BF9871564049EA5C5 /* Build configuration list for PBXNativeTarget "RDReaderView" */; + buildConfigurationList = 13F766F0FD949B8D05B6B83E2A243293 /* Build configuration list for PBXNativeTarget "RDReaderView" */; buildPhases = ( - 94D77AFA09F75B8E22FC06D6E0F38A51 /* Headers */, - F29A83019FF2B2F69579D8FCADDC0139 /* Sources */, - BAAA0ADC1A1BB29F4536DEF80F9DC8CF /* Frameworks */, - A9EF21798A8F29EF0B5258FC93ACF702 /* Resources */, + 4B2B66996212DC2632DB4C3DFB61EB18 /* Headers */, + 38EEEC488914A9E2838C8A2B024B68AE /* Sources */, + 11428808A2C31AF882AFD4536A862EC8 /* Frameworks */, + E724E9698975B2D3D91E38E41BD956E5 /* Resources */, ); buildRules = ( ); dependencies = ( - CD619BF624D423FCA6625F4BFD92B013 /* PBXTargetDependency */, - 3E7C538A5196CF397C7844CE510CC0C7 /* PBXTargetDependency */, - FC7877D22EF03521BD96324EACD5D340 /* PBXTargetDependency */, - 9EACAF83A01316321B38EC6830875522 /* PBXTargetDependency */, - 580A2BEB4003537F53B9BD73DE455D81 /* PBXTargetDependency */, + 416BC3E008F2609A8E86854EE28106CB /* PBXTargetDependency */, + 1C6859C15073631D07C87D6EC04462FF /* PBXTargetDependency */, + 6B7F8F8E9BA38033F75DB22067DAB719 /* PBXTargetDependency */, + 822DBE9158133460AC8B0C1416FDE89E /* PBXTargetDependency */, + 9CA227E3922F0FB7F722ACCF43CF24E8 /* PBXTargetDependency */, ); name = RDReaderView; productName = RDReaderView; @@ -2296,11 +2299,11 @@ }; AE7F393FB7805DE2664AB4111873F907 /* RDReaderView-RDReaderViewAssets */ = { isa = PBXNativeTarget; - buildConfigurationList = BE29401A1611B476982F60DD6B7E51BB /* Build configuration list for PBXNativeTarget "RDReaderView-RDReaderViewAssets" */; + buildConfigurationList = BAAE2E50C43325A6A6895028C447728E /* Build configuration list for PBXNativeTarget "RDReaderView-RDReaderViewAssets" */; buildPhases = ( - E85116CF32CBB9EB7BC94F1E32E05BB3 /* Sources */, - 529C6D0E8C1835AA081173D34DD20CF0 /* Frameworks */, - 1BF69E90DC79872C787637901D096FCA /* Resources */, + C678DB2A4FF718D88509B50CA13D74CC /* Sources */, + 46A46B14E5944D6717708D16DE89649F /* Frameworks */, + DF450E6A5814F170A48931CB042FC13B /* Resources */, ); buildRules = ( ); @@ -2323,8 +2326,8 @@ buildRules = ( ); dependencies = ( - 190D11763FC7F1422B28E28FB72E927B /* PBXTargetDependency */, - 7BF06EB3D3915A5482D3F7C436CF290B /* PBXTargetDependency */, + 8936F65F83E1ABA1DAA238BBE1C90C5F /* PBXTargetDependency */, + 784AD7740EFFD4E78D74D0A50F443EDA /* PBXTargetDependency */, ); name = DTCoreText; productName = DTCoreText; @@ -2333,11 +2336,11 @@ }; C7A8D82E407CD3FDC3BA55CEE519B252 /* ZIPFoundation-ZIPFoundation_Privacy */ = { isa = PBXNativeTarget; - buildConfigurationList = E26376F554F3C4B5377BA693232BB481 /* Build configuration list for PBXNativeTarget "ZIPFoundation-ZIPFoundation_Privacy" */; + buildConfigurationList = 1A78EEA4F7A0EA467A52C4ED772712D4 /* Build configuration list for PBXNativeTarget "ZIPFoundation-ZIPFoundation_Privacy" */; buildPhases = ( - 079EE1FD2DC8F78FC99B2E0D52C07C8C /* Sources */, - 5FEF2AEADB43680099867F045E9CCEB0 /* Frameworks */, - EA01657C2AC9B9A5512F3F14C98DD18B /* Resources */, + 17369C011D8091758B5785F8D4AF3ECE /* Sources */, + 5D9782EE854A9BBDB34F0D07F07A11EB /* Frameworks */, + 2C1D94BFEFBB5F74530A5E82D9BF11BB /* Resources */, ); buildRules = ( ); @@ -2388,20 +2391,26 @@ /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 1BF69E90DC79872C787637901D096FCA /* Resources */ = { + 0AD565F994D903CCC0BA1301D0C72157 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 920E76B340B80738B02FD2C582C1FE46 /* cssInjector.js in Resources */, - E7155FB098C605592505A8C976DF7901 /* epub-bridge.js in Resources */, - BD59F2A94A227D316D6BE58D23439651 /* epub-fixed-layout.html in Resources */, - CED780B0DC05E2B70B44A38853D5E0CD /* rangy-core.js in Resources */, - 4434DE1DB7F17FE76CC72C9F297FBB31 /* rangy-serializer.js in Resources */, - 8D2564C0FE31A0FC8FB2287A295C91C7 /* WeReadApi.js in Resources */, - 0367DADEDFF60CD8EC0A571F521EA7B3 /* wxread-dark.css in Resources */, - 9D0F7B1A685E8FDE76647F9B3B2F64BF /* wxread-default.css in Resources */, - 41ACA47BF5AB95B16FC884D6AE5370AB /* wxread-replace.css in Resources */, - C739C4D9352ACAF9B7203686B775E540 /* wxread-replace-latin.css in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1AF264E47FED0EDDF12198A018A67A89 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A91D815127AC86ACCE9A29979442C534 /* default.css in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2C1D94BFEFBB5F74530A5E82D9BF11BB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81114197684AE047BED887B94262BCEC /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2413,19 +2422,10 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 43FC2D4C1D214D03D882BD2AF7942FBC /* Resources */ = { + 3B9155516824ED6A3FA4D004AEE64E62 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - D424AA977964AE888E41EAC1D169DD1C /* PrivacyInfo.xcprivacy in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 712E8B02D634542EF896E9CF60FF0E43 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F4DCBD93181A99F95FDE58AEA73E53F0 /* SnapKit-SnapKit_Privacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2436,95 +2436,284 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - A9EF21798A8F29EF0B5258FC93ACF702 /* Resources */ = { + AF7C21FCC360BAF5B3E2C82C7C06EB82 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - AB684E2A99C8F730CA898A4470395362 /* RDReaderView-RDReaderViewAssets in Resources */, + DE46AF71DEEF00410A44069968B60E32 /* SnapKit-SnapKit_Privacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - B84E9DC361EC46C16A8C9ED9108D1066 /* Resources */ = { + BF9E38AF3120453D38670CA7B4B83D37 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6179B2F08A02BFAEBE23834658BE70F3 /* ZIPFoundation-ZIPFoundation_Privacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - D4E4F8471951F5B95CADF742EF6FABC7 /* Resources */ = { + DF450E6A5814F170A48931CB042FC13B /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 3C9559FB7926249C99C5925FA6A8A71F /* default.css in Resources */, + 88A1889B200CA2B4581B8F51C55857CF /* cssInjector.js in Resources */, + A2E8647998FBD87C93B2C64C4C9DD6C1 /* epub-bridge.js in Resources */, + C126E3F113976F437DCE56A43F333F01 /* epub-fixed-layout.html in Resources */, + 1C71EB408583CEAD67B9C214EC632173 /* rangy-core.js in Resources */, + 8F2D529DFB7E0B921B2232D93DD11D6F /* rangy-serializer.js in Resources */, + 4CAD70BBE240848A64180F7B1A0DCD8C /* WeReadApi.js in Resources */, + F68A4B34606ED464F14D79CA47432BDF /* wxread-dark.css in Resources */, + 1C9E8ABD096473C28D2986D3B32983B7 /* wxread-default.css in Resources */, + EA7C6CE570DA224EE0181273422A6B14 /* wxread-replace.css in Resources */, + B6934B2BD7ABDF8AC0858D96B5AF08C5 /* wxread-replace-latin.css in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - E72FA96C85146FF56A1B6704BE34A344 /* Resources */ = { + E724E9698975B2D3D91E38E41BD956E5 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - A833E24935D4F228FD3BD9BEAB740069 /* ZIPFoundation-ZIPFoundation_Privacy in Resources */, + 734D7A5703C1993676CE5460595F276F /* RDReaderView-RDReaderViewAssets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - EA01657C2AC9B9A5512F3F14C98DD18B /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 59A52D278576FC8C906A64F15F5530B3 /* PrivacyInfo.xcprivacy in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F321BEF43E22E987ECB804D91AAD23FA /* Resources */ = { + FB83DEA07B2DEDEA330961EF51164943 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 72E798049EE743AFB3351B8DD2DA36FE /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 079EE1FD2DC8F78FC99B2E0D52C07C8C /* Sources */ = { + 17369C011D8091758B5785F8D4AF3ECE /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 407323155E3E9FFEFF26E57CE4E37F74 /* Sources */ = { + 2569210201D48992A7ECE5A8C49D82FC /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 0312BB93F623A40D79BA6B5610EF1C34 /* Archive.swift in Sources */, - 484AEB14DBB75CA2E81B69C788F0C20A /* Archive+BackingConfiguration.swift in Sources */, - 89175BE2B58B4C89708B0E58C844A7AB /* Archive+Deprecated.swift in Sources */, - A053E75E8D26143FA6E7CCE25034D3DA /* Archive+Helpers.swift in Sources */, - 50CA9421DFFA956A2659DFB804B142AF /* Archive+MemoryFile.swift in Sources */, - 29E60F8ECAC68269B4263A7DCCB0EF08 /* Archive+Progress.swift in Sources */, - 761E1E1BBA67C002E2A6C74A8CECE66C /* Archive+Reading.swift in Sources */, - 70B30A5F4FE9ABD17B4367269A653E8E /* Archive+ReadingDeprecated.swift in Sources */, - 28600C55542F98933CB6752E92E0B58F /* Archive+Writing.swift in Sources */, - 6649710CC9FB92C9A52A3CAC70A2C9A0 /* Archive+WritingDeprecated.swift in Sources */, - 4CF85C5575F21F66126226F1A54A939D /* Archive+ZIP64.swift in Sources */, - D1AE34A627F20F309CC48C8989352200 /* Data+Compression.swift in Sources */, - 3C5BD179D2489DD86A9B13D20971070B /* Data+CompressionDeprecated.swift in Sources */, - 54B29D0C9C457A01271023D91E5BFDD4 /* Data+Serialization.swift in Sources */, - 098496905EA0A142F47D4418FE7C767A /* Date+ZIP.swift in Sources */, - 10F77B4635FEE3110CC1A8B1670AC582 /* Entry.swift in Sources */, - 3BB335D962947DC13E7352C7CEB56139 /* Entry+Serialization.swift in Sources */, - 69A26CBC7128559C74FA1467D2F97B65 /* Entry+ZIP64.swift in Sources */, - B10FE27CC58D55D2C18F4F71A2BE43D0 /* FileManager+ZIP.swift in Sources */, - 98EFAF63F225123A24C13391F5881BBB /* FileManager+ZIPDeprecated.swift in Sources */, - AD4D993DAAFB5D79FD9FF0730ABF4D0B /* URL+ZIP.swift in Sources */, - 69D65E345F338AD1CF34B342CBC8B986 /* ZIPFoundation-dummy.m in Sources */, + 6D893C2900F7DE1C4FD1790FFDDCE585 /* Constraint.swift in Sources */, + C96A2A5DFFCFA753C0A7558F62717D2D /* ConstraintAttributes.swift in Sources */, + 5DB252F686F6F535F3B531EF2E60692E /* ConstraintConfig.swift in Sources */, + 3EEA60E7A6E77DE25289208233488561 /* ConstraintConstantTarget.swift in Sources */, + DA4000DFBE1DD93EA927F26438A55DEE /* ConstraintDescription.swift in Sources */, + 0F1AA1FAA83F6E2D90B456429F5158AB /* ConstraintDirectionalInsets.swift in Sources */, + 9AE3623ACABFA3CF97A614D014EAFBD7 /* ConstraintDirectionalInsetTarget.swift in Sources */, + ABA0A65005939E1FB82E3B3A4F091C78 /* ConstraintDSL.swift in Sources */, + 93406FECB9741694A5C3664D2482F7D5 /* ConstraintInsets.swift in Sources */, + 860423ACC47AD21DE09C56F055DB5F28 /* ConstraintInsetTarget.swift in Sources */, + 408741E5F917D3C08E31E4AD1A6136F7 /* ConstraintItem.swift in Sources */, + B653D862AF128CEEDFD71129AE620A7D /* ConstraintLayoutGuide.swift in Sources */, + 3274F7034696EA627A4C0F934FB1D3B0 /* ConstraintLayoutGuide+Extensions.swift in Sources */, + 9C78E8CD41D34DF80AC196077012CFD7 /* ConstraintLayoutGuideDSL.swift in Sources */, + 48371ADBC5383DCCBE6E3F92DD59C6B1 /* ConstraintLayoutSupport.swift in Sources */, + F92EA25D82E65C4BE170A66D429F3B5A /* ConstraintLayoutSupportDSL.swift in Sources */, + 4550FCF88CC84EA71FA0A7AF67CC2B22 /* ConstraintMaker.swift in Sources */, + 43910F0EBE4E629A3479D75EFF08FE3F /* ConstraintMakerEditable.swift in Sources */, + 35F11E46B2193B801E96CE6CDDD4AE21 /* ConstraintMakerExtendable.swift in Sources */, + A6E152416030ADA9833117FB49E6C6A5 /* ConstraintMakerFinalizable.swift in Sources */, + 0F3D7B798A409948DBC4C48F8B673679 /* ConstraintMakerPrioritizable.swift in Sources */, + 821407D633CEB7380F5CDDADF41FCA04 /* ConstraintMakerRelatable.swift in Sources */, + 76F2DA473044593E8D856BBF41323120 /* ConstraintMakerRelatable+Extensions.swift in Sources */, + 2B0A0DE404D9534B91E22A587F11C05F /* ConstraintMultiplierTarget.swift in Sources */, + E630FEF41AB35A857F55864A56BDE8E8 /* ConstraintOffsetTarget.swift in Sources */, + A950B0683A4FD6CE58A10B2357AD603D /* ConstraintPriority.swift in Sources */, + 099BC8C8BFDA73D3998298527ACD794B /* ConstraintPriorityTarget.swift in Sources */, + A295FBBCF3BEA4000036E0415ED6C9EB /* ConstraintRelatableTarget.swift in Sources */, + C3700FAB589DC03A0A08C41B115C52AF /* ConstraintRelation.swift in Sources */, + 8983E7159E23700FEE6FCACD5E2305CB /* ConstraintView.swift in Sources */, + 4BDE693D22EC8242C611167E4B09D538 /* ConstraintView+Extensions.swift in Sources */, + 3F7362665348377483660CCEE7E5A72E /* ConstraintViewDSL.swift in Sources */, + E2D6AF839C0B606034F40459920DA24A /* Debugging.swift in Sources */, + 5D775F2253E759A5B5FDA63EDD10E725 /* LayoutConstraint.swift in Sources */, + 2BF71A220115480610FCD909D3F0C500 /* LayoutConstraintItem.swift in Sources */, + 9C672E99DC82792E146EB7630466CD20 /* SnapKit-dummy.m in Sources */, + D39500A78C93625973C27E090B69D956 /* Typealiases.swift in Sources */, + 6359AC2D2AA5C85B22CDBF3B961FEFB1 /* UILayoutSupport+Extensions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 4F6B6367A29D714DE3A990196B6E3A5A /* Sources */ = { + 38EEEC488914A9E2838C8A2B024B68AE /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4B4850CFF64032221BB0C2FBEFB7DD3E /* RDEPUBAnnotationModels.swift in Sources */, + 79156E23F0DE0D8314A7ED2C74EF5181 /* RDEPUBAssetRepository.swift in Sources */, + 3F431D135B47DE178B3D4D4E737B3F8E /* RDEPUBAttachmentNormalizer.swift in Sources */, + 2D22916B3ADAA9CF78AF3CC5F40F2E9F /* RDEPUBBackgroundTrace.swift in Sources */, + 6C874FE3A64F7F50F191A64CC2BD543F /* RDEPUBBookPageMap.swift in Sources */, + B4A84796AE5BF4ED6A1D644618A332D6 /* RDEPUBBuildDiagnosticsReporter.swift in Sources */, + 67525230780E71D60797BD57F84ACCD2 /* RDEPUBChapterCacheKey.swift in Sources */, + 3EC3CB613530B751AEB30443E1C5EEEE /* RDEPUBChapterData.swift in Sources */, + 93332352A5035E9337BEAF6373EA737F /* RDEPUBChapterDataCache.swift in Sources */, + 04E3B722AA4ACECD835F6FEAB7127684 /* RDEPUBChapterLoader.swift in Sources */, + E8452D0B6E2745B0A8BA84317F01D75D /* RDEPUBChapterLocation.swift in Sources */, + 8DE4A90F3D321F46BC078221EED31311 /* RDEPUBChapterOffsetMap.swift in Sources */, + A13DAFC3F7B4C05DCC4AF5A3FE472CC9 /* RDEPUBChapterPageCounter.swift in Sources */, + AF1180FE77173DEED2AD3291BC152E1A /* RDEPUBChapterRuntimeStore.swift in Sources */, + 88FD881543DD17A4D489DA24B1D82159 /* RDEPUBChapterSummaryDiskCache.swift in Sources */, + 8D7778083E1F47B55C1F8D00620B3D68 /* RDEPUBChapterTailNormalizer.swift in Sources */, + A8766359A57A5265AA4477967BC2D372 /* RDEPUBChapterWindowCoordinator.swift in Sources */, + 446C758763501BB1E1EC07D962A7294E /* RDEPUBChapterWindowSnapshot.swift in Sources */, + D712A797298A0126776AD6F485C7C224 /* RDEPUBCoreTextPageFrameFactory.swift in Sources */, + B505DF10D47ED5D2EE65BAE5B5BF335A /* RDEPUBDTCoreTextRenderer.swift in Sources */, + 528A1DE513701F24FD9B98742CB19706 /* RDEPUBFixedLayoutTemplate.swift in Sources */, + 845CDFFCE815068C10FD4AF0B4E15CA5 /* RDEPUBFontNormalizer.swift in Sources */, + 4DF5649A365CA463B70B022CBC72E8D3 /* RDEPUBFragmentMarkerInjector.swift in Sources */, + FFBA4341534F87364A2EC11AD286397D /* RDEPUBHTMLNormalizer.swift in Sources */, + 98A96688CD654BD1ECB83A4A6C35B2B5 /* RDEPUBJavaScriptBridge.swift in Sources */, + AEF232AD2E90CAD431398B4472B54BC5 /* RDEPUBModels.swift in Sources */, + 3E9CF41E75FE8F0F6DF4616796C901D5 /* RDEPUBNavigatorLayoutContext.swift in Sources */, + 8E3F03542AF48E8F627484F74E43F273 /* RDEPUBNavigatorState.swift in Sources */, + AA4167676147564A6AC27274F160C86B /* RDEPUBPageBreakPolicy.swift in Sources */, + 28E0E06D8E8774BE8861C32B9B48A80E /* RDEPUBPageCountCache.swift in Sources */, + C83D41DE82AB76091B97114C28172D19 /* RDEPUBPageInteractionController.swift in Sources */, + E352C433119E17F8C66192B46BF51C46 /* RDEPUBPageLayoutSnapshot.swift in Sources */, + 8F7D7DA246BD92907ECFF3E15567C653 /* RDEPUBPageResolver.swift in Sources */, + 5DC5386A66481FFAFAC58DA005F1B5FA /* RDEPUBPaginationCacheCoordinator.swift in Sources */, + 56921F586FA68A02088A7BFFB1B05319 /* RDEPUBPaginationModels.swift in Sources */, + 23159E3216F7755E8557DFDB8356B687 /* RDEPUBPaginator.swift in Sources */, + F13680D7C98C3D63DE4E1C48BB0D6629 /* RDEPUBParser.swift in Sources */, + 8731ED44361D023F68447864DF243AD8 /* RDEPUBParser+Archive.swift in Sources */, + D738CA9F50C3937A49E911CD58A59262 /* RDEPUBParser+Package.swift in Sources */, + DF0CD02EC4C094F4454701FBC1D7B1AF /* RDEPUBParser+ReadingProfile.swift in Sources */, + A6A0B209CBE2DB0BF38825BB49D9BD40 /* RDEPUBParser+Resources.swift in Sources */, + 10B2910F50DB63ED86D443844344308C /* RDEPUBParser+TOC.swift in Sources */, + EECBE7DEC22B34CA111F62AD9A487313 /* RDEPUBPreferences.swift in Sources */, + E3044B1AAB959E23ECFADBAC78250AF2 /* RDEPUBPublication.swift in Sources */, + 400BA5D4228B97F4DE8199A4CABFFDFA /* RDEPUBReaderAnnotationCoordinator.swift in Sources */, + 7E829421D4AD915E8A629AEE18B1DB39 /* RDEPUBReaderAssemblyCoordinator.swift in Sources */, + 888BAA8B73F1BCC006F04CED9B65A90F /* RDEPUBReaderBottomToolView.swift in Sources */, + F02A297AAA5834EC31C5206E18F15E7A /* RDEPUBReaderChapterListController.swift in Sources */, + D3F314D751A10537FC13519B351B2E04 /* RDEPUBReaderChromeCoordinator.swift in Sources */, + 9EA52F173814DF82FFAC811892158AF7 /* RDEPUBReaderConfiguration.swift in Sources */, + 674070237CC3D006DF6EB1F445C16362 /* RDEPUBReaderContext.swift in Sources */, + CDC18A86DDAEC766C5C19B8C5E9C95B1 /* RDEPUBReaderController.swift in Sources */, + 763BC0C9739190951DED594259EBCBD1 /* RDEPUBReaderController+ContentDelegates.swift in Sources */, + 546FA40FF62C090A815E63274A47A36C /* RDEPUBReaderController+DataSource.swift in Sources */, + B8B8E917005BED05B7FFDB7A6F7FB2B9 /* RDEPUBReaderController+PublicAPI.swift in Sources */, + C20DE912EC839E6C24E6986FD8BB54BF /* RDEPUBReaderController+RenderSupport.swift in Sources */, + 4204DDCF94EF5E57BDB32A10B8EC1BBD /* RDEPUBReaderController+RuntimeBridge.swift in Sources */, + 1BEC1F32A7BA4DD1B31433C0A2F5B493 /* RDEPUBReaderController+TableOfContents.swift in Sources */, + AF39816C18685EDACE0933D4A2FFDDCA /* RDEPUBReaderDelegate.swift in Sources */, + E1100C64BC2685999DCD2A291413C52B /* RDEPUBReaderDependencies.swift in Sources */, + 6D6C04E917D89FF0D1B06C07134A7FB4 /* RDEPUBReaderHighlightsViewController.swift in Sources */, + 0FAF20971B1067BD7F3005D2442D2A62 /* RDEPUBReaderLoadCoordinator.swift in Sources */, + 80BF3AEB1634D591238DA13EFCEB9100 /* RDEPUBReaderLocationCoordinator.swift in Sources */, + 609C3002E7FFCE826AEFFCE8805CDB49 /* RDEPUBReaderPaginationCoordinator.swift in Sources */, + 3D6535403367C804D54FFD001A77E284 /* RDEPUBReaderPersistence.swift in Sources */, + F5763770B3C5C71AECB56DCE794CACF6 /* RDEPUBReaderRuntime.swift in Sources */, + 7C2330640C0E4A460D045292C6CBC7A3 /* RDEPUBReaderSearchBarView.swift in Sources */, + E10E5848ED673CF04502FEED81F1B0D7 /* RDEPUBReaderSearchCoordinator.swift in Sources */, + DE6F45CCE420BC18B98C151A8E83BC6D /* RDEPUBReaderSettings.swift in Sources */, + C681A4D2502FBF00BF6CE3D0947B032B /* RDEPUBReaderSettingsViewController.swift in Sources */, + 99B5F1A7842B8564F8082C29DD139E50 /* RDEPUBReaderTableOfContentsItem.swift in Sources */, + 9EB7F99A75B6651DCCEC63A716AA543B /* RDEPUBReaderTheme.swift in Sources */, + D0B50C95B7602E93C6813B56F9909F3A /* RDEPUBReaderToolView.swift in Sources */, + 9446AB65083BF63454F0BC79597D7A70 /* RDEPUBReaderTopToolView.swift in Sources */, + 5B38E03FBC5FA066303609D347C9406B /* RDEPUBReaderUIState.swift in Sources */, + 5FE984F0E5484F951ADC5A6918DAE7CA /* RDEPUBReaderViewportMonitor.swift in Sources */, + 021C7AEF8B20CD2494A1DB2BD22D0ABA /* RDEPUBReadingLocationModels.swift in Sources */, + 627E8C3508550607CB5A183DFA7CBEF1 /* RDEPUBReadingSession.swift in Sources */, + AAF33DBF3EDE2E462850017949EB0A59 /* RDEPUBRenderDiagnosticsCollector.swift in Sources */, + CA7AD3BB11AECE506ADDF8C25F279904 /* RDEPUBRenderRequest.swift in Sources */, + 684C12A054C3831A1E74B0C9EEE7AE33 /* RDEPUBResourceResolver.swift in Sources */, + 7D692EFA1ED7DF2CE3E867BCE6926A28 /* RDEPUBResourceURLSchemeHandler.swift in Sources */, + 71D5FDB9307694CB527C67D15D30157D /* RDEPUBRuntimeChapter.swift in Sources */, + 9F9C7582F35DDD1AFB5AB289D5BCA7D5 /* RDEPUBRuntimePageCount.swift in Sources */, + 2D2EA7C7CEEE4FDB9B6E4E55D4C16B12 /* RDEPUBSearchEngine.swift in Sources */, + ACB17BD54175BC3B98AC1175481E6AED /* RDEPUBSearchModels.swift in Sources */, + 42761D3507E4C730BDBDDA969D0BD369 /* RDEPUBSelectionOverlayView.swift in Sources */, + C38BA41ED0310D484DE9BC50683409A1 /* RDEPUBSelectionState.swift in Sources */, + 394F47614B158E6EB298F25D0B17E289 /* RDEPUBSemanticMarkerInjector.swift in Sources */, + A337239791E84EF6E8715CBCE689637C /* RDEPUBStyleSheetBuilder.swift in Sources */, + 4DEA8CEF63598961120FBE983CD3E18B /* RDEPUBStyleSheetComposer.swift in Sources */, + 0BBE0292EC0E79F1D9548CDA1C22F2D8 /* RDEPUBTextAnchor.swift in Sources */, + 7C0F477270950490612CFDFD2463B894 /* RDEPUBTextAnnotationOverlay.swift in Sources */, + 7934ABD31F35415F9A2C8276676A6648 /* RDEPUBTextBookBuilder.swift in Sources */, + 3ECDB3138484C8DD487C06D90F3764DB /* RDEPUBTextBookCache.swift in Sources */, + B510C7A442F6FE2E206976EFFAC000FD /* RDEPUBTextBookModels.swift in Sources */, + CA67E186452744CFE975CDC8CF75640F /* RDEPUBTextBuildPipelineInterfaces.swift in Sources */, + C80A0D159BF6BC3D103A63B0302293B9 /* RDEPUBTextContentView.swift in Sources */, + CC63E1E8EA43990861286233CB21034A /* RDEPUBTextIndexTable.swift in Sources */, + E8A8019AA3033E666C0E67B6C6C4DAF3 /* RDEPUBTextLayouter.swift in Sources */, + 42D25E859E8E34B7837070F521533223 /* RDEPUBTextLayoutFrame.swift in Sources */, + 95F7823641FEAB9FDCC40C27199DAAAB /* RDEPUBTextPageDecorationView.swift in Sources */, + E50DB2DE7D674E6FBFB6D4AF5255A84F /* RDEPUBTextPageRenderView.swift in Sources */, + 00B5E59F71F71A71B769FD3DDBDAF965 /* RDEPUBTextPaginationInterfaces.swift in Sources */, + 6EAC401461BC8CFFC1A85021F3EAAB00 /* RDEPUBTextPaginationSupport.swift in Sources */, + F14DD745C5ED6624BDAA23CF1C7AA23B /* RDEPUBTextPerformanceSampler.swift in Sources */, + 742187DFE94AAB5DF65DCCDFFB9A2519 /* RDEPUBTextPositionConverter.swift in Sources */, + 8706619D1EE7AD70C069BFA14EAAC295 /* RDEPUBTextRenderer.swift in Sources */, + A8D8E9F0F2486DA5E8B6BB91285E23ED /* RDEPUBTextRendererSupport.swift in Sources */, + 9C0255A17E35943423E7D7AB969AC7F6 /* RDEPUBTextSearchEngine.swift in Sources */, + 016371375864889F69517D55CDA3A266 /* RDEPUBTextSelectionController.swift in Sources */, + A01919793CF5504D2A234FCFEF12B961 /* RDEPUBTypesettingPipeline.swift in Sources */, + 91740868C9193AB08E7FA6E1330B8DAA /* RDEPUBViewportTypes.swift in Sources */, + 9BC6941C2AFB6676D1D700EA2F6C96F3 /* RDEPUBWebContentView.swift in Sources */, + 7A8499BDE42C8ABC4739C5BAEDA0C3A7 /* RDEPUBWebDecorationOverlayView.swift in Sources */, + B9F653EC455154C78DE01B878E886926 /* RDEPUBWebView.swift in Sources */, + 9C3E94E23AE8C0AD6111BCF14AEC76D8 /* RDEPUBWebView+Configuration.swift in Sources */, + 25DA73F3FE71BA016BE8F3673C677DCC /* RDEPUBWebView+FixedLayout.swift in Sources */, + 0A8A60254CCFFFB540A977E422004D68 /* RDEPUBWebView+JavaScriptBridge.swift in Sources */, + 9B5B3177B11F60D56864D6B23599A1A4 /* RDEPUBWebView+Reflowable.swift in Sources */, + 9AF5A960FC572176D3B411E21E5C15E3 /* RDEPUBWebView+Search.swift in Sources */, + F3A9470F157B110967AB1544A7BD0DCB /* RDEPUBWebViewDebug.swift in Sources */, + 043A2E9478F84524117CC70002023DFF /* RDPlainTextBookBuilder.swift in Sources */, + C6D061E40B11A03C582F08F0FE658A7B /* RDReaderContentCell.swift in Sources */, + 086DCEC7AEBFD0464283CCEFAF12ADF0 /* RDReaderFlowLayout.swift in Sources */, + 3F59AAA6847628B5D101968C5B0FC0E2 /* RDReaderGestureController.swift in Sources */, + 7492D552569049B3C77F482254C81141 /* RDReaderPageChildViewController.swift in Sources */, + 6A8605EAC81CC09EC9AD3FA8D5CBCB76 /* RDReaderPagingController.swift in Sources */, + E6BC4DC52C8602301A04EEBC41A7C54D /* RDReaderPreloadController.swift in Sources */, + 12C9A6EB078364B73087AFBC96A8AAF9 /* RDReaderSpreadResolver.swift in Sources */, + 8C1D391880A42513665477B22E50398E /* RDReaderTapRegionHandler.swift in Sources */, + 8A1237A80B1E82607F28B0B7FEC62AA7 /* RDReaderView.swift in Sources */, + 1BE56F06EBF770B5C5CC88E777A185DE /* RDReaderView+CollectionView.swift in Sources */, + 55DA9AC6E71D27CF7C8D4FE3A247BD0B /* RDReaderView+ContentAccess.swift in Sources */, + 6AD0EA5E10820A6B99B38A511DEF6F12 /* RDReaderView+PageCurl.swift in Sources */, + 43DB7BD38DD98A921E1886DFB837B388 /* RDReaderView+ToolView.swift in Sources */, + B6FF3D1C24E6AF28D71A0CD7F5F18189 /* RDReaderView-dummy.m in Sources */, + 630146BD032D6366A273E5F74C33A863 /* RDReaderViewProtocols.swift in Sources */, + 3F47CD5028C4FAFCB02D90173FD7BE13 /* RDURLReaderController.swift in Sources */, + 0E4376280494B1718140645948A07E24 /* String+SHA256.swift in Sources */, + 31FC7230D5C6B0306855EE5087D0A26F /* UIColor+RDEPUBHex.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 404DE8BB8ADE877FE966E92F9757E95C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 095FEA4761F0931E859306E016FE9D46 /* Archive.swift in Sources */, + DC16500BB2DC867D159E2B4795FD2E00 /* Archive+BackingConfiguration.swift in Sources */, + 87C52B306599974254236AC5016E4AAD /* Archive+Deprecated.swift in Sources */, + 567A0978AA797E4385F13B23465F3585 /* Archive+Helpers.swift in Sources */, + 855F8BD571BD8EB0F5E7756AC2413C40 /* Archive+MemoryFile.swift in Sources */, + D9619B2333335421D6BAEE499C684389 /* Archive+Progress.swift in Sources */, + 5B268BA451F57A14CBB953F3CC488566 /* Archive+Reading.swift in Sources */, + 4135416AE5B0306A47A967018A2CB7B8 /* Archive+ReadingDeprecated.swift in Sources */, + 03646B79FE3DB509B26BEF05E4E7C31F /* Archive+Writing.swift in Sources */, + 5E82688424A62DA1557419401D47B340 /* Archive+WritingDeprecated.swift in Sources */, + 99EED2CEABDAD89BA879A10A967AF73C /* Archive+ZIP64.swift in Sources */, + DF811FAAD2A380EF8C303151A273183A /* Data+Compression.swift in Sources */, + 4B2FDE4A4976B62F9FCE6CE8BC1AEE94 /* Data+CompressionDeprecated.swift in Sources */, + AED36CEE168D0B0F38E9278A6072D11D /* Data+Serialization.swift in Sources */, + DC9D87AC6D207651A205478E4595A360 /* Date+ZIP.swift in Sources */, + C35E6F015F735469B32D4B64F92B06EF /* Entry.swift in Sources */, + 8A8C466E9265295CFB7A9BC165D2F6C4 /* Entry+Serialization.swift in Sources */, + C94D3CDEE5621CEE53D409852A4882BD /* Entry+ZIP64.swift in Sources */, + B01557AA9F0C45612AE1C7ADB1CCE4FC /* FileManager+ZIP.swift in Sources */, + 014E4415B0D09135065AC238E6414DEA /* FileManager+ZIPDeprecated.swift in Sources */, + 28402E474AD3C0223488D3AF7C0D159B /* URL+ZIP.swift in Sources */, + F4FB9D77B89F3CE38F95B2B9C8BDC780 /* ZIPFoundation-dummy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2536,225 +2725,79 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 9B88ED6B8C1D60DE29790A2CBAEE55A8 /* Sources */ = { + 98DB2E7FF91A6985191703CBEB926865 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1CF76515B052AD77C383F1A3F79F777A /* SSAlertAnimation.swift in Sources */, - 512F03D47C95781C8187E080E49177B0 /* SSAlertAnimationController.swift in Sources */, - 6C000EA66E8FA9A388BA73E62C421EAB /* SSAlertCommonView.swift in Sources */, - A5633B025E0AA8C922E35775EA0AC5E0 /* SSAlertDefaultAnmation.swift in Sources */, - 999B9B214FCBD51D40BDA3C1310D3867 /* SSAlertPresentAnimation.swift in Sources */, - FD81151580A55178103D6CCC596A9574 /* SSAlertSwift-dummy.m in Sources */, - 4948AB438B6A2316B0137CFDEE0E30CF /* SSAlertView.swift in Sources */, - 70561AD84616F0AEC6D3FE835A6F74A7 /* SSAlertViewExtention.swift in Sources */, - 6093C9FC371FB067097DD5D0D6731DFC /* UIViewFrameExtension.swift in Sources */, + 5A8E43AB3D398143BB25F39B222A18FE /* SSAlertAnimation.swift in Sources */, + 54B7A61A6E6A76A14DD71273E5E6B101 /* SSAlertAnimationController.swift in Sources */, + DBC2DD8B83F5404F00CC1481D2FA70ED /* SSAlertCommonView.swift in Sources */, + E3EB2F12234336BBC7BD786BF4B15EA8 /* SSAlertDefaultAnmation.swift in Sources */, + 0D1F6D0CA56ABD2B42038C4088FB5BB5 /* SSAlertPresentAnimation.swift in Sources */, + ADA3C68B75F77F86802CE889179486CD /* SSAlertSwift-dummy.m in Sources */, + 6F774A3E91C081DC41C567F7673AB0C2 /* SSAlertView.swift in Sources */, + 5636D02C578B36C6C52095DEEE012662 /* SSAlertViewExtention.swift in Sources */, + 24A4EC565C31C36075596144B8F44299 /* UIViewFrameExtension.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - BA36A5FE12DEB6B1C60C9352E1BB07DA /* Sources */ = { + B2F045F25878E43023D094161DD9F5B6 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - E85116CF32CBB9EB7BC94F1E32E05BB3 /* Sources */ = { + B81ADB90CEB0C2BC5C3A5EB71B740B22 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9753446AFAFB2D5206BC3815BC726AC7 /* DTActivityTitleView.m in Sources */, + 0DD55F7F8057B47C6B71DDD3EFDBBFEA /* DTAnimatedGIF.m in Sources */, + 52978358028473BE63B6B1F6AA20675C /* DTBase64Coding.m in Sources */, + FFF409F0205F1745B8B3AC89917F07A3 /* DTBlockFunctions.m in Sources */, + E052221C319BA638FAA2C82325694B42 /* DTCoreGraphicsUtils.m in Sources */, + 14EAF9CBECC154CC00AACDD3224EC979 /* DTCustomColoredAccessory.m in Sources */, + F9DBAFE16D14195BDCFBAAC4FDE3CFD4 /* DTExtendedFileAttributes.m in Sources */, + 1446BE1A7579119303BE0FE7D371DA14 /* DTFolderMonitor.m in Sources */, + 31FFF99A325707F94A86A18AA48FF162 /* DTFoundation-dummy.m in Sources */, + 567FA56050A61292B9224271986EF937 /* DTFoundationConstants.m in Sources */, + 6E71A9CA46078CFBC110C52B11389A4B /* DTHTMLParser.m in Sources */, + CFA89FA29A6C84CD39425D4D1C97F8C2 /* DTLog.m in Sources */, + EB1183B3D404746EBC0FB8949E816BA4 /* DTPieProgressIndicator.m in Sources */, + A4E30E61CCDF03F8ABBCA6E261A9158A /* DTSmartPagingScrollView.m in Sources */, + FBE148A72A9BB6C0E85F0AADDF975FB8 /* DTTiledLayerWithoutFade.m in Sources */, + D67FD0F33281FD59AEDE6A35DEDCCA80 /* DTVersion.m in Sources */, + 6B95FC01A9B50EC9A6276642AF561CE2 /* NSArray+DTError.m in Sources */, + 9CEF7F2CF595080103EEE04A25BED20D /* NSData+DTCrypto.m in Sources */, + 16F6ACFCF57F6DAC2706790DA7242E9A /* NSDictionary+DTError.m in Sources */, + EE5DD52AC6049A014EB83D27D68F8A0E /* NSFileWrapper+DTCopying.m in Sources */, + 7234AF3FEE44D88C2FA4B78EB30391F1 /* NSMutableArray+DTMoving.m in Sources */, + 07A7ADAEF645213B401323F269E7CC56 /* NSString+DTFormatNumbers.m in Sources */, + A625C258A5EB2D38A3B9160DD77FDD8E /* NSString+DTPaths.m in Sources */, + E0FE3B76A6ABF85CFED6D5E8E98E7CD4 /* NSString+DTURLEncoding.m in Sources */, + E048208FDCFDEAEF6DBB9B40DB89D77A /* NSString+DTUtilities.m in Sources */, + 9C0E5A978B5A27AC8C09ED68B9764422 /* NSURL+DTAppLinks.m in Sources */, + 96207BE20EB66606E46D23F61D098367 /* NSURL+DTComparing.m in Sources */, + 970824C814CB8C9FBEC8916A75542AF3 /* NSURL+DTUnshorten.m in Sources */, + 5D9E814BF6DF6BCE6842249F8A93DBC2 /* UIApplication+DTNetworkActivity.m in Sources */, + DAD3AB7B9072C8C4A948BBFF1EBE45B0 /* UIImage+DTFoundation.m in Sources */, + 3C037B1C09894A3F5F84B3496DD6849A /* UIScreen+DTFoundation.m in Sources */, + 172F60ACE858151BB15425047802D38F /* UIView+DTFoundation.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C678DB2A4FF718D88509B50CA13D74CC /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - F29A83019FF2B2F69579D8FCADDC0139 /* Sources */ = { + D14625340E2861C36773F9C4484DFD0C /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9463C65355C0D7F791DDFEBD7665DDF8 /* RDEPUBAnnotationModels.swift in Sources */, - 5C537C41B6A15AC9F87DA0CD3F99B362 /* RDEPUBAssetRepository.swift in Sources */, - A1A80C07F342BF3A07CA033606A6A552 /* RDEPUBAttachmentNormalizer.swift in Sources */, - 388702D454B856FB6B85047F469EA482 /* RDEPUBBackgroundTrace.swift in Sources */, - FE539F115EB066FEC060F71C7772CDF1 /* RDEPUBBookPageMap.swift in Sources */, - 1649C327967B95DEB0F8E3E3315E5D81 /* RDEPUBBuildDiagnosticsReporter.swift in Sources */, - 132602B42D91E7EBB0B757F6FCDB215B /* RDEPUBChapterCacheKey.swift in Sources */, - F2D41D78F78CC4596D14A0484C85B5EF /* RDEPUBChapterData.swift in Sources */, - E8A9845F331539C2091F5DDD08C0A848 /* RDEPUBChapterDataCache.swift in Sources */, - 4043D3783C54C47FD0481A4A1B55247F /* RDEPUBChapterLoader.swift in Sources */, - 8B95E586217609BACEFF85D7C5849C42 /* RDEPUBChapterLocation.swift in Sources */, - F0614580B5D04710FC659553B52609CC /* RDEPUBChapterOffsetMap.swift in Sources */, - 6F3BBAE11AD5528F26A3EAB35A3D8ECF /* RDEPUBChapterPageCounter.swift in Sources */, - 6EA446E553C33A1F676CE6B3C7ACE147 /* RDEPUBChapterRuntimeStore.swift in Sources */, - 504A9738EBFE1D9E4DC0023D92AE8F73 /* RDEPUBChapterSummaryDiskCache.swift in Sources */, - DB867D24B763CC5A0FCC4B37E4CF822B /* RDEPUBChapterTailNormalizer.swift in Sources */, - 0F668A5D4700EDA52CEEEF065C42BCA4 /* RDEPUBChapterWindowCoordinator.swift in Sources */, - EDAC7618BC92FC4B459F158E06BDD6C7 /* RDEPUBChapterWindowSnapshot.swift in Sources */, - 9D78CCA3439AC9188A5C183A9A00D473 /* RDEPUBCoreTextPageFrameFactory.swift in Sources */, - 6AB69D332406B3820CA8165641F52C90 /* RDEPUBDTCoreTextRenderer.swift in Sources */, - 2767D03964C6AD5DF426A37648BE2DCE /* RDEPUBFixedLayoutTemplate.swift in Sources */, - BFF12CDB6761C929D95D99E0BD1AC32D /* RDEPUBFontNormalizer.swift in Sources */, - 988B59EBD2A33A2FD9A02193C13C8CAB /* RDEPUBFragmentMarkerInjector.swift in Sources */, - BE123FB6EA9D908EFAC4B1F8C2DA44E5 /* RDEPUBHTMLNormalizer.swift in Sources */, - ED021F861A73CA0AE92DBA03C972F296 /* RDEPUBJavaScriptBridge.swift in Sources */, - D6BFB6720734FB0EC3D287CB03A28AD4 /* RDEPUBLocationConverter.swift in Sources */, - 1F919D0C4FAC68D245BBAE657EDCE900 /* RDEPUBModels.swift in Sources */, - EF2F5AAF11933BBE5B1CCEAA829954B1 /* RDEPUBNavigatorLayoutContext.swift in Sources */, - ADC53AFF68D3D9CBBCBCFC5D4DD97303 /* RDEPUBNavigatorState.swift in Sources */, - 9D551B01D2565BB99465813B43D9A73B /* RDEPUBPageBreakPolicy.swift in Sources */, - BB08A213FC1D81987E2B1094CCA018E0 /* RDEPUBPageCountCache.swift in Sources */, - 02042C62EF343DD67D81BE25DC028830 /* RDEPUBPageInteractionController.swift in Sources */, - B7CEA88BC794B1C614273D425F1E5048 /* RDEPUBPageLayoutSnapshot.swift in Sources */, - FE5D015FCE8DAE214ED92143FCCDDA10 /* RDEPUBPageResolver.swift in Sources */, - D356836F1638F813B0F4C5F8AAA150C6 /* RDEPUBPaginationCacheCoordinator.swift in Sources */, - 0422D9463811398A8B8106DFC5151DAB /* RDEPUBPaginationModels.swift in Sources */, - 75489D9A756E9D0FE1BCEF136DB94072 /* RDEPUBPaginator.swift in Sources */, - 6917152800EF84858313CDAE5EB2A480 /* RDEPUBParser.swift in Sources */, - 221A6546C685365590F113AD63E50BC1 /* RDEPUBParser+Archive.swift in Sources */, - 4EB15D9BDFFCDFE35C6FC81E71C761CD /* RDEPUBParser+Package.swift in Sources */, - A8E83D12A6E410D0FC8A5DBE43C52590 /* RDEPUBParser+ReadingProfile.swift in Sources */, - 05818B0A9C710EF83D4E1A525E87FA79 /* RDEPUBParser+Resources.swift in Sources */, - 913E1174EACC09B744C3FFEEFF542B1E /* RDEPUBParser+TOC.swift in Sources */, - A9D844A2F27BA98D3BA75873EDD65C59 /* RDEPUBPreferences.swift in Sources */, - AF751E58599AA3DDF6E0E044A31C9E61 /* RDEPUBPublication.swift in Sources */, - BC211BF30DBBDAC998C506BEAFABD491 /* RDEPUBReaderAnnotationCoordinator.swift in Sources */, - AB9C03C2161D535EA9BB736D9EB8B049 /* RDEPUBReaderAssemblyCoordinator.swift in Sources */, - 032C67D09A34F3EDFEFAA1B665F3D45A /* RDEPUBReaderBottomToolView.swift in Sources */, - 5633438CD9B1C71CC74EE0A85717E313 /* RDEPUBReaderChapterListController.swift in Sources */, - 40AC0BE626808D9D04B82C8F334BC2BC /* RDEPUBReaderChromeCoordinator.swift in Sources */, - B8517869241495A627A33561CB97D369 /* RDEPUBReaderConfiguration.swift in Sources */, - 5B26943069B0F5425BBE23E5A3F8F30A /* RDEPUBReaderContext.swift in Sources */, - 3BE1CD7DD55BECCC7C5DE5F962DD020A /* RDEPUBReaderController.swift in Sources */, - 20FF4D9CA3EC29F7B0E7F5A3FDE14A4A /* RDEPUBReaderController+ContentDelegates.swift in Sources */, - 2DC6F25E5B1AC15B1041E5129E9DB5DC /* RDEPUBReaderController+DataSource.swift in Sources */, - 0F64292129E8BCF4B2D6E475A354FAA8 /* RDEPUBReaderController+PublicAPI.swift in Sources */, - 9FFC1191E3FE7A42DB62B217A165374A /* RDEPUBReaderController+RenderSupport.swift in Sources */, - 79321BE2258B95B63DB7427BE14A1AE9 /* RDEPUBReaderController+RuntimeBridge.swift in Sources */, - 3D2DAB8E64826A368B9284D8640322EC /* RDEPUBReaderController+TableOfContents.swift in Sources */, - E62C939CE97E5B1E751569F1FAAB082A /* RDEPUBReaderDelegate.swift in Sources */, - F2694378DEBE2779E51DE1C817FD21C6 /* RDEPUBReaderDependencies.swift in Sources */, - 814EF5142E9C6BBE23F1F4AD50A12345 /* RDEPUBReaderHighlightsViewController.swift in Sources */, - 1C08C84B9A7C1E693309BC43AA1E0063 /* RDEPUBReaderLoadCoordinator.swift in Sources */, - 807CA77A74C46495868ECECF394C0908 /* RDEPUBReaderLocationCoordinator.swift in Sources */, - 3800D19E7EC5920B171087AE11B25B31 /* RDEPUBReaderPaginationCoordinator.swift in Sources */, - 3736065BFCBF19E580DB5EC8FA17B884 /* RDEPUBReaderPersistence.swift in Sources */, - FE128B6AE2BC2AF0F05B3A0878D17B06 /* RDEPUBReaderRuntime.swift in Sources */, - 14257DD9075535547195291198A4B56B /* RDEPUBReaderSearchBarView.swift in Sources */, - AC349599AF6B7B81AB3D4B4430893BCC /* RDEPUBReaderSearchCoordinator.swift in Sources */, - 80FDABF695B4F0B44F772FE30E4A224C /* RDEPUBReaderSettings.swift in Sources */, - 1C82DEA8CBD206778DB821B9A05C83AE /* RDEPUBReaderSettingsViewController.swift in Sources */, - 7E75DCB27C929A478CAC307A7729EE29 /* RDEPUBReaderTableOfContentsItem.swift in Sources */, - 55B8D3CC4726C45665A8A5ADCB0A7F79 /* RDEPUBReaderTheme.swift in Sources */, - 13C2DDE4E717A3E4CC202933AA8BDA50 /* RDEPUBReaderToolView.swift in Sources */, - BD4A60A13E46761D3FBDA962A5237091 /* RDEPUBReaderTopToolView.swift in Sources */, - 38D07F9682C100D89684B0A57731BF21 /* RDEPUBReaderViewportMonitor.swift in Sources */, - 9E446579CEB5E8BC164603FE7CE15101 /* RDEPUBReadingLocationModels.swift in Sources */, - C4EB180D0B3B630F5A7A2B1583B5E794 /* RDEPUBReadingSession.swift in Sources */, - A1BF991FC54C5FD49E55A52E6A0F2F7C /* RDEPUBRenderDiagnosticsCollector.swift in Sources */, - 3A3FB95E5C631A93B0E25C0E9AFB3BF4 /* RDEPUBRenderRequest.swift in Sources */, - A50B62E0566BA314560E1819BAB75818 /* RDEPUBResourceResolver.swift in Sources */, - 2823587BD92CCD62B4A002B4B61E391B /* RDEPUBResourceURLSchemeHandler.swift in Sources */, - 46BD36B7EB1CBF0BB1E4AD4C84B516F4 /* RDEPUBRuntimeChapter.swift in Sources */, - 288D7FCEDD998F32DEBCB08A8FFB0DAE /* RDEPUBRuntimePageCount.swift in Sources */, - 2F25AF09403C69487847A7ADD6645B88 /* RDEPUBSearchEngine.swift in Sources */, - 9BCA2AC7CB0DE92A10C51B78F496D67B /* RDEPUBSearchModels.swift in Sources */, - D72E29B6BDF79056DB0196324B801659 /* RDEPUBSelectionOverlayView.swift in Sources */, - 1D989BC9B2C48DF57ECCD9D275967ED1 /* RDEPUBSemanticMarkerInjector.swift in Sources */, - 17EC27F89C86E3BFDA6595D92C9B99DF /* RDEPUBStyleSheetBuilder.swift in Sources */, - D6A349786DE991EE40FA199ABEF53AE2 /* RDEPUBStyleSheetComposer.swift in Sources */, - FE58F512BA753D11477A7116C3D5082D /* RDEPUBTextAnchor.swift in Sources */, - 194CC2A6339C6632772193C86071B332 /* RDEPUBTextAnnotationOverlay.swift in Sources */, - B50EA676BE4541E31653213E59CCAB38 /* RDEPUBTextBookBuilder.swift in Sources */, - CFF1C5073FEB03082CCD3F8CBC662F6E /* RDEPUBTextBookCache.swift in Sources */, - 9673AF7E9761F76F91633267FE6955EC /* RDEPUBTextBookModels.swift in Sources */, - 1C332AD59B3889359CBF39BA7BCECA50 /* RDEPUBTextBuildPipelineInterfaces.swift in Sources */, - 1FB0FCC893FA187DBB7B49718B787AA8 /* RDEPUBTextContentView.swift in Sources */, - 2404CA703443CC7DF42099D57B7846C7 /* RDEPUBTextIndexTable.swift in Sources */, - 8C051D63625D0279E70383D7939259F8 /* RDEPUBTextLayouter.swift in Sources */, - A7F8C570D541E1F1EC1A8E4A2EC2E68C /* RDEPUBTextLayoutFrame.swift in Sources */, - B4D3AE83F584470109BF585432C6AF7D /* RDEPUBTextPageDecorationView.swift in Sources */, - B12F0E87FEAB61906615AA24A89C7868 /* RDEPUBTextPageRenderView.swift in Sources */, - 9E5EE2F88FD00579AB3DDBF7AD8A0AE8 /* RDEPUBTextPaginationInterfaces.swift in Sources */, - FE8CA7EF8AA87C97549223C9A02251F7 /* RDEPUBTextPaginationSupport.swift in Sources */, - 0A5EB141623189C9CA54D72EFC964858 /* RDEPUBTextPerformanceSampler.swift in Sources */, - 753FB374E263E515914EEA8A41D41B7F /* RDEPUBTextPositionConverter.swift in Sources */, - 5BAD2BFBCB3FB2505892A5A1480A7225 /* RDEPUBTextRenderer.swift in Sources */, - 4C2E3E6443766A6673F6913B9EC97D38 /* RDEPUBTextRendererSupport.swift in Sources */, - 109BE5337A3F49126904D50447B0A895 /* RDEPUBTextSearchEngine.swift in Sources */, - E26725CBBF02CE92FE5DB944A79F6732 /* RDEPUBTextSelectionController.swift in Sources */, - 086D9D0020342C633405486DB9FFC40B /* RDEPUBTypesettingPipeline.swift in Sources */, - BF779F9500C6DF3A569494C8830C96B0 /* RDEPUBViewportTypes.swift in Sources */, - 8622AC205E7BC1315C2610BFE74A788F /* RDEPUBWebContentView.swift in Sources */, - 7419CE4BFAFC651C051B867282FE2297 /* RDEPUBWebDecorationOverlayView.swift in Sources */, - 7A082386EEF40DC6056A27F530FF7B43 /* RDEPUBWebView.swift in Sources */, - C7FFD3B0C1516632BD8D3E4868C61E79 /* RDEPUBWebView+Configuration.swift in Sources */, - B749C708327563022F4ABF12CF3A9E94 /* RDEPUBWebView+FixedLayout.swift in Sources */, - 2BDA9B95A38F510B33098480B1CF2BFF /* RDEPUBWebView+JavaScriptBridge.swift in Sources */, - 17534667DC33BD803AE0B9652F9B8A9F /* RDEPUBWebView+Reflowable.swift in Sources */, - 18D86CDB39D4048E28706CE956113303 /* RDEPUBWebView+Search.swift in Sources */, - 3922A859DCEE60641247531983AF7749 /* RDEPUBWebViewDebug.swift in Sources */, - FB02F142A6ED6908D3AEC074B4CB113D /* RDPlainTextBookBuilder.swift in Sources */, - 8EBC8430CBF44F64B4FACADBD0B8EF6B /* RDReaderContentCell.swift in Sources */, - D5A0CAEBA0B0F55C2C0F885EE8B3B69E /* RDReaderFlowLayout.swift in Sources */, - 5358DCB22018E30C798BEF35B60B1818 /* RDReaderGestureController.swift in Sources */, - 0FCBA3933EE0E5A478045C9C51B903E7 /* RDReaderPageChildViewController.swift in Sources */, - 31E359EFE25014EEF231198256A4A55D /* RDReaderPagingController.swift in Sources */, - 1CF962A07C17085793B630F12C39D0AB /* RDReaderPreloadController.swift in Sources */, - 881B7BD0998D85E16EFD2844EAD4770B /* RDReaderSpreadResolver.swift in Sources */, - 99C4B9A6CC62724B7BFBF8ADB17C618E /* RDReaderTapRegionHandler.swift in Sources */, - 1DB70691E03C930D4382CAEECA12A97B /* RDReaderView.swift in Sources */, - D35237860CD654AAE9D67BBDC710A4B8 /* RDReaderView+CollectionView.swift in Sources */, - 49B23C37C1793A7BBD73EB21CD9A1F82 /* RDReaderView+ContentAccess.swift in Sources */, - 0D7A3666C06D8695D703BF8A93C467BC /* RDReaderView+PageCurl.swift in Sources */, - 01D50E7898F9ECCFE0703106D7A12066 /* RDReaderView+ToolView.swift in Sources */, - F8BF98E83C90D04AA3379C9A4623D7E2 /* RDReaderView-dummy.m in Sources */, - 9DD8B22C4E99C2A8303A6EEAE802E5CC /* RDReaderViewProtocols.swift in Sources */, - D42D898AD123808E26CFA93BDA62F8AC /* RDURLReaderController.swift in Sources */, - AE54C8FD8A185D0635D6977951DF5AEE /* String+SHA256.swift in Sources */, - 6AB3FDA2AFB7EE7AAFDED21DB7DC0675 /* UIColor+RDEPUBHex.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F41891DC36E4B8E47C081F0DB953978A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 106B7BA59F8A3091A8AD453E71824882 /* Constraint.swift in Sources */, - 5A2947B6387888CB9CACE6A670ECDB45 /* ConstraintAttributes.swift in Sources */, - 79E2065263D1E448793F1CCFE2293ECE /* ConstraintConfig.swift in Sources */, - AF6200E5AABBF295D6FA3E41957E322C /* ConstraintConstantTarget.swift in Sources */, - 46FE133228B81200750001BFACC79E89 /* ConstraintDescription.swift in Sources */, - BABB04BF50AC580EC52084CA431CCF71 /* ConstraintDirectionalInsets.swift in Sources */, - 37B41230C263BE09177B0FAD184D2B02 /* ConstraintDirectionalInsetTarget.swift in Sources */, - 8F6A2AAAB01E57DF77A12326E5C90DE6 /* ConstraintDSL.swift in Sources */, - 8B7DBE63942CFE22AA7A29AF8C18BC83 /* ConstraintInsets.swift in Sources */, - 235CE890F1B1C5530A6D0ABF73AAAC66 /* ConstraintInsetTarget.swift in Sources */, - 43F2F5FDA2FC0C6E0F25EAECA626C6BF /* ConstraintItem.swift in Sources */, - 27326E81ED7191074A965F1906994720 /* ConstraintLayoutGuide.swift in Sources */, - 7075B2D4BF3C6A47BB9906A508D769F5 /* ConstraintLayoutGuide+Extensions.swift in Sources */, - 75E391C6630E7493AF23660F84A2A2F4 /* ConstraintLayoutGuideDSL.swift in Sources */, - 988E47B526BE07E81833417799E2833B /* ConstraintLayoutSupport.swift in Sources */, - DBD5BC69F5BF488734E93EB1591A7455 /* ConstraintLayoutSupportDSL.swift in Sources */, - B098641C2F013BC6531D283D239D9B61 /* ConstraintMaker.swift in Sources */, - 0F4E87E30789C356E774477D867F6F23 /* ConstraintMakerEditable.swift in Sources */, - 1C59E564A3D3B215FDD80CB84FCF9B24 /* ConstraintMakerExtendable.swift in Sources */, - B57E7C8E379A4A4EF754FC7072D33A39 /* ConstraintMakerFinalizable.swift in Sources */, - 952F5C1C0DD1FE08A2A9B95457457814 /* ConstraintMakerPrioritizable.swift in Sources */, - 035D32F76982E769321736F9AE645A39 /* ConstraintMakerRelatable.swift in Sources */, - 9D3DD0AC19334C572E7F2B4E6AFE0E6C /* ConstraintMakerRelatable+Extensions.swift in Sources */, - 77AF3653D38EE679BD8A00B497FAE32C /* ConstraintMultiplierTarget.swift in Sources */, - 65002F5F1273AF0CBE8367B073F8661B /* ConstraintOffsetTarget.swift in Sources */, - C6AF83C094C274A61C7CB4062A3943DC /* ConstraintPriority.swift in Sources */, - 549B5989FE7A52A0E861D46E2450F118 /* ConstraintPriorityTarget.swift in Sources */, - 498305D5FF380F875B4FCF28218E2FED /* ConstraintRelatableTarget.swift in Sources */, - 24F57F63118933065AC2E2E13BFFFDA6 /* ConstraintRelation.swift in Sources */, - 09E75B00712A3C1368E7DA2819A9906A /* ConstraintView.swift in Sources */, - 7DBDA591B91EDB5C187DDE335235E5A6 /* ConstraintView+Extensions.swift in Sources */, - 472C6BD678C2FCD8E50E4D9CD00B74E8 /* ConstraintViewDSL.swift in Sources */, - 92BA0540B424C459F31036AF32CADA5E /* Debugging.swift in Sources */, - 62567CA27B6255EA5850BB5CEC9B7543 /* LayoutConstraint.swift in Sources */, - F6A3CC6F64BE9D2E5AD45D2821196111 /* LayoutConstraintItem.swift in Sources */, - A3A69DDFE22FBC1F582BC14B6126830C /* SnapKit-dummy.m in Sources */, - 6438FB9A11267C525E9F8A5E2A077C33 /* Typealiases.swift in Sources */, - 950FB487525188C210A2F377151C00BA /* UILayoutSupport+Extensions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2827,162 +2870,105 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - FF9D1B44C98BC984EAA82543F0F12BD4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 0F21BA3B21E2267D70F0B6285143BBE9 /* DTActivityTitleView.m in Sources */, - 00B6BBA8B3A4551F0B349467EE60B357 /* DTAnimatedGIF.m in Sources */, - E651CDB1967286FAB7D7B68291FF6A86 /* DTBase64Coding.m in Sources */, - CD0A44B836906158422FD41F5F86A0A3 /* DTBlockFunctions.m in Sources */, - 887C1C25E7B69AB387761C1BAE23EEFB /* DTCoreGraphicsUtils.m in Sources */, - 88259EAF08B1418D155966286E26DF34 /* DTCustomColoredAccessory.m in Sources */, - E77F49061B5055BDBBB16114D7D4E882 /* DTExtendedFileAttributes.m in Sources */, - B2A8DFD01894EA8B42B8E42E3057799A /* DTFolderMonitor.m in Sources */, - A9826B61AF63CFD1C4CCF19C9FD08662 /* DTFoundation-dummy.m in Sources */, - 5D701F265B0D067A5441F932FD23056D /* DTFoundationConstants.m in Sources */, - 025AE2757712862E5524EEDB2525C5CD /* DTHTMLParser.m in Sources */, - AD539146F04C1A6F229006A5388A8EDD /* DTLog.m in Sources */, - 78A25C8BD48D75A9395327659E683D36 /* DTPieProgressIndicator.m in Sources */, - 68BB5D9462ECD306DC249D2F65983016 /* DTSmartPagingScrollView.m in Sources */, - 19D0D49617A8039B4932A0784A647FC2 /* DTTiledLayerWithoutFade.m in Sources */, - E67F588B1E229877C200B96784118C44 /* DTVersion.m in Sources */, - 9C2186EA4B21504D6507CE501BABFBF4 /* NSArray+DTError.m in Sources */, - 93538FC1DF1B0A675282F8C0433A1266 /* NSData+DTCrypto.m in Sources */, - CA832E0649A6753992FEFCD55E1B778B /* NSDictionary+DTError.m in Sources */, - F5EB0B62950DEEE558D889D1003AF432 /* NSFileWrapper+DTCopying.m in Sources */, - 0F0AB96A761DEDB65E66F94864C9C880 /* NSMutableArray+DTMoving.m in Sources */, - AB36563CC3EDC235699CCEB6D1484066 /* NSString+DTFormatNumbers.m in Sources */, - C99F2011C8DC83BC6FC00055B08011D2 /* NSString+DTPaths.m in Sources */, - 8DF6E80E2F1AD0902D7270BCC0C74298 /* NSString+DTURLEncoding.m in Sources */, - DF8FDCB60CF766E247D02C515719A4A7 /* NSString+DTUtilities.m in Sources */, - 9CB1F58C6CCE41A375FEEFE9C1C59795 /* NSURL+DTAppLinks.m in Sources */, - 6284B2D1444FFA41C6E54990657E576B /* NSURL+DTComparing.m in Sources */, - 607E7C43D068045DA32EAA3E0B947305 /* NSURL+DTUnshorten.m in Sources */, - 1BB0F7065230323728EB5205C7E96E9E /* UIApplication+DTNetworkActivity.m in Sources */, - DBF505F4ACCE320791D8152854E9354D /* UIImage+DTFoundation.m in Sources */, - 0A16E0D7CB8036C3C10A4F0A361ECB85 /* UIScreen+DTFoundation.m in Sources */, - 0FC6DE303CFDF04D3340415EABBA2B56 /* UIView+DTFoundation.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 190D11763FC7F1422B28E28FB72E927B /* PBXTargetDependency */ = { + 13585C5584DA3266D4A24DDD9B31AD55 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = "DTCoreText-Resources"; - target = 0C24CB0E87A728A11AA1124CB360D6A1 /* DTCoreText-Resources */; - targetProxy = A67A76B78EB9B8A3BFABD5C713E7BA63 /* PBXContainerItemProxy */; + name = DTCoreText; + target = B88F4EA0695B6B3165C64594850D72C7 /* DTCoreText */; + targetProxy = F0FA44D7E63FF42A6C497438761F3B77 /* PBXContainerItemProxy */; }; - 26D085C3E077507E2B80DC3E912CFE56 /* PBXTargetDependency */ = { + 197FB2078B81A644D08043561984F27E /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = SnapKit; - target = 19622742EBA51E823D6DAE3F8CDBFAD4 /* SnapKit */; - targetProxy = E5375B19EF8369B17DD045C5C6AB9590 /* PBXContainerItemProxy */; + name = SSAlertSwift; + target = 8619D5ADECF2B26CFD9A9826D61D289A /* SSAlertSwift */; + targetProxy = A3F5D978334155622A97BB4625069CA1 /* PBXContainerItemProxy */; }; - 3E7C538A5196CF397C7844CE510CC0C7 /* PBXTargetDependency */ = { + 1C6859C15073631D07C87D6EC04462FF /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "RDReaderView-RDReaderViewAssets"; target = AE7F393FB7805DE2664AB4111873F907 /* RDReaderView-RDReaderViewAssets */; - targetProxy = AA3ADC832F3674BDD5726D99641EB9E5 /* PBXContainerItemProxy */; + targetProxy = DD757D1119D7FD9CFB9803BB8F180DF0 /* PBXContainerItemProxy */; }; - 56B1FF14CF990DE2239DEC252C19E5F8 /* PBXTargetDependency */ = { + 2E712D8870E5EBCA7A222177E8449AC1 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = DTFoundation; target = 8F6E5A5BF72D62CDFD25F91A7CFA3309 /* DTFoundation */; - targetProxy = 854B45A7C1AAF89785F301AF0E6D33C7 /* PBXContainerItemProxy */; + targetProxy = E21CAAA5EA7C676E5E305C3B1B88D982 /* PBXContainerItemProxy */; }; - 580A2BEB4003537F53B9BD73DE455D81 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = ZIPFoundation; - target = AA15C8469D67684160CC2A7098EB841C /* ZIPFoundation */; - targetProxy = B73BD2CE6BDD286244B88561CE2C3E44 /* PBXContainerItemProxy */; - }; - 645796323263BF71ED05ED05D44F93CD /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = SSAlertSwift; - target = 8619D5ADECF2B26CFD9A9826D61D289A /* SSAlertSwift */; - targetProxy = C5B70D53764B58C82081EE2758B8790A /* PBXContainerItemProxy */; - }; - 73CF3460790CE3E276D2BF53928EACFF /* PBXTargetDependency */ = { + 416BC3E008F2609A8E86854EE28106CB /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = DTCoreText; target = B88F4EA0695B6B3165C64594850D72C7 /* DTCoreText */; - targetProxy = 2FEB97D4CCD7A71799CA75E1BF5AD6AA /* PBXContainerItemProxy */; + targetProxy = A9CA67AE9091496AECCD25CE058E0EB4 /* PBXContainerItemProxy */; }; - 777A2E94D64DA72CF95884726BC24EEA /* PBXTargetDependency */ = { + 52F662FBC328D9ABFA736CA337E4A2F7 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = "SnapKit-SnapKit_Privacy"; - target = 8A8DB685241263AFDF5E6B20FE67B93A /* SnapKit-SnapKit_Privacy */; - targetProxy = F6BF41ACECDA86F5762F7F276E86BD1B /* PBXContainerItemProxy */; + name = ZIPFoundation; + target = AA15C8469D67684160CC2A7098EB841C /* ZIPFoundation */; + targetProxy = A51E17C7DB24793DA4AD107C12EC554D /* PBXContainerItemProxy */; }; - 7BF06EB3D3915A5482D3F7C436CF290B /* PBXTargetDependency */ = { + 6B7F8F8E9BA38033F75DB22067DAB719 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = DTFoundation; - target = 8F6E5A5BF72D62CDFD25F91A7CFA3309 /* DTFoundation */; - targetProxy = A3AFCE91FFD7D4DCCD796B7E018FA543 /* PBXContainerItemProxy */; + name = SSAlertSwift; + target = 8619D5ADECF2B26CFD9A9826D61D289A /* SSAlertSwift */; + targetProxy = 2CDE2C5C25B94E29D0E991B83FCC525D /* PBXContainerItemProxy */; }; - 8082ADB56D3087F46C2C3AE8276C048E /* PBXTargetDependency */ = { + 7128F364EDA5D95565A3FF0D037494D5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "ZIPFoundation-ZIPFoundation_Privacy"; target = C7A8D82E407CD3FDC3BA55CEE519B252 /* ZIPFoundation-ZIPFoundation_Privacy */; - targetProxy = 9F1F7047ABEB860A15FC928201C5E0E5 /* PBXContainerItemProxy */; + targetProxy = DE06E9EC48201B0A1486D4E40137598D /* PBXContainerItemProxy */; }; - 9EACAF83A01316321B38EC6830875522 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = SnapKit; - target = 19622742EBA51E823D6DAE3F8CDBFAD4 /* SnapKit */; - targetProxy = 715CA2D4D1E6CFF1F48AA221E8CC1B40 /* PBXContainerItemProxy */; - }; - A35B91F3BDE34ED4C32DF403989B4C30 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = ZIPFoundation; - target = AA15C8469D67684160CC2A7098EB841C /* ZIPFoundation */; - targetProxy = 15BED367A216D80838EB7C12554E0653 /* PBXContainerItemProxy */; - }; - CD619BF624D423FCA6625F4BFD92B013 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = DTCoreText; - target = B88F4EA0695B6B3165C64594850D72C7 /* DTCoreText */; - targetProxy = 4282060097D663E353F2B35BA95A21B2 /* PBXContainerItemProxy */; - }; - DD6B5CE86D7AD6AF30BA7C3C7FE438BA /* PBXTargetDependency */ = { + 7700CDB869DDB5BC16AA1C5083D98C45 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RDReaderView; target = AA2E57587AA8EECA63C4BE08EA3CB6D2 /* RDReaderView */; - targetProxy = 0054D393C28B889558DF0D00390F5769 /* PBXContainerItemProxy */; + targetProxy = D2195D28B2165E92956DD46A264456AF /* PBXContainerItemProxy */; }; - FC7877D22EF03521BD96324EACD5D340 /* PBXTargetDependency */ = { + 77D3AA74C59602B92A392CFC63E44F0F /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = SSAlertSwift; - target = 8619D5ADECF2B26CFD9A9826D61D289A /* SSAlertSwift */; - targetProxy = 3BC6E6A89A1BD3072A8FBC04721656F7 /* PBXContainerItemProxy */; + name = "SnapKit-SnapKit_Privacy"; + target = 8A8DB685241263AFDF5E6B20FE67B93A /* SnapKit-SnapKit_Privacy */; + targetProxy = AAA9B1811E0CF13CD7ED2D08F75A4F29 /* PBXContainerItemProxy */; + }; + 784AD7740EFFD4E78D74D0A50F443EDA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = DTFoundation; + target = 8F6E5A5BF72D62CDFD25F91A7CFA3309 /* DTFoundation */; + targetProxy = 1C5F512837ED73C03A0016C6C71019B6 /* PBXContainerItemProxy */; + }; + 822DBE9158133460AC8B0C1416FDE89E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SnapKit; + target = 19622742EBA51E823D6DAE3F8CDBFAD4 /* SnapKit */; + targetProxy = AB7EFADB0158CA56FA326C1085D102D9 /* PBXContainerItemProxy */; + }; + 8936F65F83E1ABA1DAA238BBE1C90C5F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "DTCoreText-Resources"; + target = 0C24CB0E87A728A11AA1124CB360D6A1 /* DTCoreText-Resources */; + targetProxy = 29937A7B398A9C3E1C58905655B3DB1E /* PBXContainerItemProxy */; + }; + 9CA227E3922F0FB7F722ACCF43CF24E8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = ZIPFoundation; + target = AA15C8469D67684160CC2A7098EB841C /* ZIPFoundation */; + targetProxy = 5DE91FF74FCD48024A2680A11AA59C68 /* PBXContainerItemProxy */; + }; + CAB448101F2C74E72C200BE40545771B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SnapKit; + target = 19622742EBA51E823D6DAE3F8CDBFAD4 /* SnapKit */; + targetProxy = 2074E58B021F8317A9C99BF7BA77373D /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - 068C1C143EB3459F56BB37B8D8F99099 /* Release */ = { + 06C978693AF79C8163C013B909FF6880 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2940F45AAC94F45A257576176011B6A4 /* RDReaderView.release.xcconfig */; - buildSettings = { - CODE_SIGNING_ALLOWED = NO; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/RDReaderView"; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - IBSC_MODULE = RDReaderView; - INFOPLIST_FILE = "Target Support Files/RDReaderView/ResourceBundle-RDReaderViewAssets-RDReaderView-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - PRODUCT_NAME = RDReaderViewAssets; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = bundle; - }; - name = Release; - }; - 0AA63E1B433EB306302A10588A6BB49E /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 18387159515E0523AFB4204B1AF940ED /* SSAlertSwift.debug.xcconfig */; + baseConfigurationReference = FE2991699D0391172DEADF2F4613E320 /* SSAlertSwift.release.xcconfig */; buildSettings = { "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; @@ -3013,16 +2999,16 @@ SWIFT_INSTALL_OBJC_HEADER = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Debug; + name = Release; }; - 0EA3FA7B1BD01215319CD8F0D7752B83 /* Debug */ = { + 0A3320760CDB1EE16ADC2DDB1E5ABF31 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C960B7AA0B9B8CC473FACFEC64F9906D /* SnapKit.debug.xcconfig */; + baseConfigurationReference = 18387159515E0523AFB4204B1AF940ED /* SSAlertSwift.debug.xcconfig */; buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; @@ -3033,9 +3019,9 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_MODULE_VERIFIER = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_PREFIX_HEADER = "Target Support Files/SnapKit/SnapKit-prefix.pch"; + GCC_PREFIX_HEADER = "Target Support Files/SSAlertSwift/SSAlertSwift-prefix.pch"; GENERATE_INFOPLIST_FILE = NO; - INFOPLIST_FILE = "Target Support Files/SnapKit/SnapKit-Info.plist"; + INFOPLIST_FILE = "Target Support Files/SSAlertSwift/SSAlertSwift-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( @@ -3043,9 +3029,9 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MODULEMAP_FILE = "Target Support Files/SnapKit/SnapKit.modulemap"; - PRODUCT_MODULE_NAME = SnapKit; - PRODUCT_NAME = SnapKit; + MODULEMAP_FILE = "Target Support Files/SSAlertSwift/SSAlertSwift.modulemap"; + PRODUCT_MODULE_NAME = SSAlertSwift; + PRODUCT_NAME = SSAlertSwift; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; @@ -3097,7 +3083,46 @@ }; name = Release; }; - 16B91FB6308BDFA09170029BB2FDE65F /* Release */ = { + 1D8CFC223CA5C3F8046ACDB2299C8C9C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C960B7AA0B9B8CC473FACFEC64F9906D /* SnapKit.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_PREFIX_HEADER = "Target Support Files/SnapKit/SnapKit-prefix.pch"; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = "Target Support Files/SnapKit/SnapKit-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/SnapKit/SnapKit.modulemap"; + PRODUCT_MODULE_NAME = SnapKit; + PRODUCT_NAME = SnapKit; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_INSTALL_OBJC_HEADER = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 1DB154855B1112DB6C33ADE407C07DA5 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = DF670EC438F05ACC1F1C7B725DBDAA18 /* DTFoundation.release.xcconfig */; buildSettings = { @@ -3137,47 +3162,7 @@ }; name = Release; }; - 264434368DB52108702A2EB43B2AEFC6 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 53D2AB2348A9B9C582FDC2CDB2E434FC /* SnapKit.release.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_PREFIX_HEADER = "Target Support Files/SnapKit/SnapKit-prefix.pch"; - GENERATE_INFOPLIST_FILE = NO; - INFOPLIST_FILE = "Target Support Files/SnapKit/SnapKit-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/SnapKit/SnapKit.modulemap"; - PRODUCT_MODULE_NAME = SnapKit; - PRODUCT_NAME = SnapKit; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_INSTALL_OBJC_HEADER = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 307F23F0CD7303916F3578733BB1C587 /* Debug */ = { + 240E5FE10CED2E96C91D9366A84A6E19 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 4BEC60CB3EDA1C189D3B062E29217440 /* ZIPFoundation.debug.xcconfig */; buildSettings = { @@ -3216,6 +3201,81 @@ }; name = Debug; }; + 35E3BD51E582481D9C568BFF064AC051 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F699881ABDBEB419AEBA661AD5A21F35 /* ZIPFoundation.release.xcconfig */; + buildSettings = { + CODE_SIGNING_ALLOWED = NO; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/ZIPFoundation"; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + IBSC_MODULE = ZIPFoundation; + INFOPLIST_FILE = "Target Support Files/ZIPFoundation/ResourceBundle-ZIPFoundation_Privacy-ZIPFoundation-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + PRODUCT_NAME = ZIPFoundation_Privacy; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; + 3612CA8B78C271E39F158A37A6388244 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2940F45AAC94F45A257576176011B6A4 /* RDReaderView.release.xcconfig */; + buildSettings = { + CODE_SIGNING_ALLOWED = NO; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/RDReaderView"; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + IBSC_MODULE = RDReaderView; + INFOPLIST_FILE = "Target Support Files/RDReaderView/ResourceBundle-RDReaderViewAssets-RDReaderView-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + PRODUCT_NAME = RDReaderViewAssets; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; + 388B95820F9ADA93341B7ED00A7B8A6F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6014DA7213E4687224F2BF921E30002C /* DTFoundation.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_PREFIX_HEADER = "Target Support Files/DTFoundation/DTFoundation-prefix.pch"; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = "Target Support Files/DTFoundation/DTFoundation-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/DTFoundation/DTFoundation.modulemap"; + PRODUCT_MODULE_NAME = DTFoundation; + PRODUCT_NAME = DTFoundation; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_INSTALL_OBJC_HEADER = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; 44A4B2FB6211B90C1949A44A381F6D1F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { @@ -3279,124 +3339,9 @@ }; name = Release; }; - 4BEBBEE4473AF129932138FCC61304F9 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 2940F45AAC94F45A257576176011B6A4 /* RDReaderView.release.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_PREFIX_HEADER = "Target Support Files/RDReaderView/RDReaderView-prefix.pch"; - GENERATE_INFOPLIST_FILE = NO; - INFOPLIST_FILE = "Target Support Files/RDReaderView/RDReaderView-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/RDReaderView/RDReaderView.modulemap"; - PRODUCT_MODULE_NAME = RDReaderView; - PRODUCT_NAME = RDReaderView; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_INSTALL_OBJC_HEADER = YES; - SWIFT_VERSION = 5.10; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 5EB3FF5E18CF1A954E1D019BDF4D3B6B /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = FE2991699D0391172DEADF2F4613E320 /* SSAlertSwift.release.xcconfig */; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_PREFIX_HEADER = "Target Support Files/SSAlertSwift/SSAlertSwift-prefix.pch"; - GENERATE_INFOPLIST_FILE = NO; - INFOPLIST_FILE = "Target Support Files/SSAlertSwift/SSAlertSwift-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/SSAlertSwift/SSAlertSwift.modulemap"; - PRODUCT_MODULE_NAME = SSAlertSwift; - PRODUCT_NAME = SSAlertSwift; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_INSTALL_OBJC_HEADER = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 616C2438ADA48E1279CE059476CB84A1 /* Release */ = { + 524D9B863153923A6945F0E35F7EEF37 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = F699881ABDBEB419AEBA661AD5A21F35 /* ZIPFoundation.release.xcconfig */; - buildSettings = { - CODE_SIGNING_ALLOWED = NO; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/ZIPFoundation"; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - IBSC_MODULE = ZIPFoundation; - INFOPLIST_FILE = "Target Support Files/ZIPFoundation/ResourceBundle-ZIPFoundation_Privacy-ZIPFoundation-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - PRODUCT_NAME = ZIPFoundation_Privacy; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = bundle; - }; - name = Release; - }; - 677E9D48412EF5A26E8F164E0D943322 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 3867F0F0A2292C20B5E5F8A564133F59 /* RDReaderView.debug.xcconfig */; - buildSettings = { - CODE_SIGNING_ALLOWED = NO; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/RDReaderView"; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - IBSC_MODULE = RDReaderView; - INFOPLIST_FILE = "Target Support Files/RDReaderView/ResourceBundle-RDReaderViewAssets-RDReaderView-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - PRODUCT_NAME = RDReaderViewAssets; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = bundle; - }; - name = Debug; - }; - 6B6B3051460DB5C58184867FCC4F6FBB /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 6014DA7213E4687224F2BF921E30002C /* DTFoundation.debug.xcconfig */; buildSettings = { CLANG_ENABLE_OBJC_WEAK = NO; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; @@ -3409,9 +3354,9 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_MODULE_VERIFIER = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_PREFIX_HEADER = "Target Support Files/DTFoundation/DTFoundation-prefix.pch"; + GCC_PREFIX_HEADER = "Target Support Files/ZIPFoundation/ZIPFoundation-prefix.pch"; GENERATE_INFOPLIST_FILE = NO; - INFOPLIST_FILE = "Target Support Files/DTFoundation/DTFoundation-Info.plist"; + INFOPLIST_FILE = "Target Support Files/ZIPFoundation/ZIPFoundation-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( @@ -3419,19 +3364,20 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MODULEMAP_FILE = "Target Support Files/DTFoundation/DTFoundation.modulemap"; - PRODUCT_MODULE_NAME = DTFoundation; - PRODUCT_NAME = DTFoundation; + MODULEMAP_FILE = "Target Support Files/ZIPFoundation/ZIPFoundation.modulemap"; + PRODUCT_MODULE_NAME = ZIPFoundation; + PRODUCT_NAME = ZIPFoundation; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_INSTALL_OBJC_HEADER = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Debug; + name = Release; }; 73BBB08AD96AC1A6A095E15ADC5FCEE1 /* Debug */ = { isa = XCBuildConfiguration; @@ -3472,9 +3418,66 @@ }; name = Debug; }; - 93772559EE6FF1DEDA9F81892EBEF8A9 /* Debug */ = { + 83688406F27077A29FAC5187A607100A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 78873FAFC0AD9E930D6050A7F976714B /* DTCoreText.debug.xcconfig */; + baseConfigurationReference = 3867F0F0A2292C20B5E5F8A564133F59 /* RDReaderView.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_PREFIX_HEADER = "Target Support Files/RDReaderView/RDReaderView-prefix.pch"; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = "Target Support Files/RDReaderView/RDReaderView-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/RDReaderView/RDReaderView.modulemap"; + PRODUCT_MODULE_NAME = RDReaderView; + PRODUCT_NAME = RDReaderView; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_INSTALL_OBJC_HEADER = YES; + SWIFT_VERSION = 5.10; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 8FF6C5389A72B553C7D344092E315A18 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4BEC60CB3EDA1C189D3B062E29217440 /* ZIPFoundation.debug.xcconfig */; + buildSettings = { + CODE_SIGNING_ALLOWED = NO; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/ZIPFoundation"; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + IBSC_MODULE = ZIPFoundation; + INFOPLIST_FILE = "Target Support Files/ZIPFoundation/ResourceBundle-ZIPFoundation_Privacy-ZIPFoundation-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + PRODUCT_NAME = ZIPFoundation_Privacy; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + 91E2E7825CD3C7BEE81E7524E4709460 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C2840BE6D91479CCBC2E908FBD9DA183 /* DTCoreText.release.xcconfig */; buildSettings = { CODE_SIGNING_ALLOWED = NO; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/DTCoreText"; @@ -3488,25 +3491,7 @@ TARGETED_DEVICE_FAMILY = "1,2"; WRAPPER_EXTENSION = bundle; }; - name = Debug; - }; - A078901DC412445D8DC84180D2B50B31 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = C960B7AA0B9B8CC473FACFEC64F9906D /* SnapKit.debug.xcconfig */; - buildSettings = { - CODE_SIGNING_ALLOWED = NO; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/SnapKit"; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - IBSC_MODULE = SnapKit; - INFOPLIST_FILE = "Target Support Files/SnapKit/ResourceBundle-SnapKit_Privacy-SnapKit-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - PRODUCT_NAME = SnapKit_Privacy; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = bundle; - }; - name = Debug; + name = Release; }; A0DC1C2C814704734F83EAB35BB20B21 /* Debug */ = { isa = XCBuildConfiguration; @@ -3654,85 +3639,9 @@ }; name = Debug; }; - A9C0540FBA212FDE35C55067430B4555 /* Release */ = { + A906B39116AB6E78E68CD880304D1D19 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C2840BE6D91479CCBC2E908FBD9DA183 /* DTCoreText.release.xcconfig */; - buildSettings = { - CODE_SIGNING_ALLOWED = NO; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/DTCoreText"; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - IBSC_MODULE = DTCoreText; - INFOPLIST_FILE = "Target Support Files/DTCoreText/ResourceBundle-Resources-DTCoreText-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - PRODUCT_NAME = Resources; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = bundle; - }; - name = Release; - }; - B02EA15679215F7FB733433435B5BA8F /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = F699881ABDBEB419AEBA661AD5A21F35 /* ZIPFoundation.release.xcconfig */; - buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_PREFIX_HEADER = "Target Support Files/ZIPFoundation/ZIPFoundation-prefix.pch"; - GENERATE_INFOPLIST_FILE = NO; - INFOPLIST_FILE = "Target Support Files/ZIPFoundation/ZIPFoundation-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MODULEMAP_FILE = "Target Support Files/ZIPFoundation/ZIPFoundation.modulemap"; - PRODUCT_MODULE_NAME = ZIPFoundation; - PRODUCT_NAME = ZIPFoundation; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_INSTALL_OBJC_HEADER = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - B057B14222A0FF3FD884EB3405A2D962 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 53D2AB2348A9B9C582FDC2CDB2E434FC /* SnapKit.release.xcconfig */; - buildSettings = { - CODE_SIGNING_ALLOWED = NO; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/SnapKit"; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - IBSC_MODULE = SnapKit; - INFOPLIST_FILE = "Target Support Files/SnapKit/ResourceBundle-SnapKit_Privacy-SnapKit-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; - PRODUCT_NAME = SnapKit_Privacy; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = bundle; - }; - name = Release; - }; - D4DDA52B6CF1F782D478FAAFBC02BA15 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 3867F0F0A2292C20B5E5F8A564133F59 /* RDReaderView.debug.xcconfig */; + baseConfigurationReference = 2940F45AAC94F45A257576176011B6A4 /* RDReaderView.release.xcconfig */; buildSettings = { CLANG_ENABLE_OBJC_WEAK = NO; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; @@ -3764,22 +3673,23 @@ SWIFT_INSTALL_OBJC_HEADER = YES; SWIFT_VERSION = 5.10; TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Debug; + name = Release; }; - DB9766AFA03D018F06C25500B43CDEBB /* Debug */ = { + B958376CF15160F7D63EF9D7E4F1A4EE /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4BEC60CB3EDA1C189D3B062E29217440 /* ZIPFoundation.debug.xcconfig */; + baseConfigurationReference = 3867F0F0A2292C20B5E5F8A564133F59 /* RDReaderView.debug.xcconfig */; buildSettings = { CODE_SIGNING_ALLOWED = NO; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/ZIPFoundation"; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/RDReaderView"; ENABLE_USER_SCRIPT_SANDBOXING = NO; - IBSC_MODULE = ZIPFoundation; - INFOPLIST_FILE = "Target Support Files/ZIPFoundation/ResourceBundle-ZIPFoundation_Privacy-ZIPFoundation-Info.plist"; + IBSC_MODULE = RDReaderView; + INFOPLIST_FILE = "Target Support Files/RDReaderView/ResourceBundle-RDReaderViewAssets-RDReaderView-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 15.6; - PRODUCT_NAME = ZIPFoundation_Privacy; + PRODUCT_NAME = RDReaderViewAssets; SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; @@ -3787,6 +3697,100 @@ }; name = Debug; }; + BEEB532A9447E11F867E914D7D9818E4 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 78873FAFC0AD9E930D6050A7F976714B /* DTCoreText.debug.xcconfig */; + buildSettings = { + CODE_SIGNING_ALLOWED = NO; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/DTCoreText"; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + IBSC_MODULE = DTCoreText; + INFOPLIST_FILE = "Target Support Files/DTCoreText/ResourceBundle-Resources-DTCoreText-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + PRODUCT_NAME = Resources; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + E23BC9529330441CEDBB70BB5C6BCDC6 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C960B7AA0B9B8CC473FACFEC64F9906D /* SnapKit.debug.xcconfig */; + buildSettings = { + CODE_SIGNING_ALLOWED = NO; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/SnapKit"; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + IBSC_MODULE = SnapKit; + INFOPLIST_FILE = "Target Support Files/SnapKit/ResourceBundle-SnapKit_Privacy-SnapKit-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + PRODUCT_NAME = SnapKit_Privacy; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + EA75F3AC6625670740DDFD9D07C69566 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 53D2AB2348A9B9C582FDC2CDB2E434FC /* SnapKit.release.xcconfig */; + buildSettings = { + CODE_SIGNING_ALLOWED = NO; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/SnapKit"; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + IBSC_MODULE = SnapKit; + INFOPLIST_FILE = "Target Support Files/SnapKit/ResourceBundle-SnapKit_Privacy-SnapKit-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + PRODUCT_NAME = SnapKit_Privacy; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; + EC72A6AB64EA684B29084E446260C2AB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 53D2AB2348A9B9C582FDC2CDB2E434FC /* SnapKit.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_PREFIX_HEADER = "Target Support Files/SnapKit/SnapKit-prefix.pch"; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = "Target Support Files/SnapKit/SnapKit-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/SnapKit/SnapKit.modulemap"; + PRODUCT_MODULE_NAME = SnapKit; + PRODUCT_NAME = SnapKit; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_INSTALL_OBJC_HEADER = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -3799,29 +3803,29 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 29BF4529A766777BF9871564049EA5C5 /* Build configuration list for PBXNativeTarget "RDReaderView" */ = { + 13F766F0FD949B8D05B6B83E2A243293 /* Build configuration list for PBXNativeTarget "RDReaderView" */ = { isa = XCConfigurationList; buildConfigurations = ( - D4DDA52B6CF1F782D478FAAFBC02BA15 /* Debug */, - 4BEBBEE4473AF129932138FCC61304F9 /* Release */, + 83688406F27077A29FAC5187A607100A /* Debug */, + A906B39116AB6E78E68CD880304D1D19 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 2DAE2EE6565CA746E723D0C94409A244 /* Build configuration list for PBXNativeTarget "DTCoreText-Resources" */ = { + 1A78EEA4F7A0EA467A52C4ED772712D4 /* Build configuration list for PBXNativeTarget "ZIPFoundation-ZIPFoundation_Privacy" */ = { isa = XCConfigurationList; buildConfigurations = ( - 93772559EE6FF1DEDA9F81892EBEF8A9 /* Debug */, - A9C0540FBA212FDE35C55067430B4555 /* Release */, + 8FF6C5389A72B553C7D344092E315A18 /* Debug */, + 35E3BD51E582481D9C568BFF064AC051 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 36824C5E16CF085752ED979AE1202BE2 /* Build configuration list for PBXNativeTarget "SnapKit-SnapKit_Privacy" */ = { + 2A92134767212E0AF1CE27B20F859A21 /* Build configuration list for PBXNativeTarget "DTFoundation" */ = { isa = XCConfigurationList; buildConfigurations = ( - A078901DC412445D8DC84180D2B50B31 /* Debug */, - B057B14222A0FF3FD884EB3405A2D962 /* Release */, + 388B95820F9ADA93341B7ED00A7B8A6F /* Debug */, + 1DB154855B1112DB6C33ADE407C07DA5 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -3835,38 +3839,56 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 4A07C786C7B54AD03891D3FF6D6C3BED /* Build configuration list for PBXNativeTarget "SnapKit" */ = { + 490DB4724C304AF051ED35D068A20B47 /* Build configuration list for PBXNativeTarget "SnapKit-SnapKit_Privacy" */ = { isa = XCConfigurationList; buildConfigurations = ( - 0EA3FA7B1BD01215319CD8F0D7752B83 /* Debug */, - 264434368DB52108702A2EB43B2AEFC6 /* Release */, + E23BC9529330441CEDBB70BB5C6BCDC6 /* Debug */, + EA75F3AC6625670740DDFD9D07C69566 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 975641A0350D40E81E27E9F94D6367E1 /* Build configuration list for PBXNativeTarget "ZIPFoundation" */ = { + 5165AACF46A63B4FB8A8420ACC759DD3 /* Build configuration list for PBXNativeTarget "SnapKit" */ = { isa = XCConfigurationList; buildConfigurations = ( - 307F23F0CD7303916F3578733BB1C587 /* Debug */, - B02EA15679215F7FB733433435B5BA8F /* Release */, + 1D8CFC223CA5C3F8046ACDB2299C8C9C /* Debug */, + EC72A6AB64EA684B29084E446260C2AB /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 99AFD3DA8C698FBE12BF062246D560F5 /* Build configuration list for PBXNativeTarget "SSAlertSwift" */ = { + A7E7C52E8356B363E4C668C76D72C91E /* Build configuration list for PBXNativeTarget "DTCoreText-Resources" */ = { isa = XCConfigurationList; buildConfigurations = ( - 0AA63E1B433EB306302A10588A6BB49E /* Debug */, - 5EB3FF5E18CF1A954E1D019BDF4D3B6B /* Release */, + BEEB532A9447E11F867E914D7D9818E4 /* Debug */, + 91E2E7825CD3C7BEE81E7524E4709460 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - BE29401A1611B476982F60DD6B7E51BB /* Build configuration list for PBXNativeTarget "RDReaderView-RDReaderViewAssets" */ = { + A8B16500C77E14435BD81E2FC3C99BCD /* Build configuration list for PBXNativeTarget "SSAlertSwift" */ = { isa = XCConfigurationList; buildConfigurations = ( - 677E9D48412EF5A26E8F164E0D943322 /* Debug */, - 068C1C143EB3459F56BB37B8D8F99099 /* Release */, + 0A3320760CDB1EE16ADC2DDB1E5ABF31 /* Debug */, + 06C978693AF79C8163C013B909FF6880 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + AA6CCE7B84211FC7AD7D89714B12CE1E /* Build configuration list for PBXNativeTarget "ZIPFoundation" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 240E5FE10CED2E96C91D9366A84A6E19 /* Debug */, + 524D9B863153923A6945F0E35F7EEF37 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BAAE2E50C43325A6A6895028C447728E /* Build configuration list for PBXNativeTarget "RDReaderView-RDReaderViewAssets" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B958376CF15160F7D63EF9D7E4F1A4EE /* Debug */, + 3612CA8B78C271E39F158A37A6388244 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -3880,24 +3902,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - DBBE36D4A67C028942B51F5CFD5154ED /* Build configuration list for PBXNativeTarget "DTFoundation" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 6B6B3051460DB5C58184867FCC4F6FBB /* Debug */, - 16B91FB6308BDFA09170029BB2FDE65F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - E26376F554F3C4B5377BA693232BB481 /* Build configuration list for PBXNativeTarget "ZIPFoundation-ZIPFoundation_Privacy" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - DB9766AFA03D018F06C25500B43CDEBB /* Debug */, - 616C2438ADA48E1279CE059476CB84A1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ }; rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; diff --git a/ReadViewDemo/ReadViewDemo.xcodeproj/project.pbxproj b/ReadViewDemo/ReadViewDemo.xcodeproj/project.pbxproj index e20c4bc..4fa744d 100644 --- a/ReadViewDemo/ReadViewDemo.xcodeproj/project.pbxproj +++ b/ReadViewDemo/ReadViewDemo.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 1A2B3C4D00000011AABBCC01 /* DemoReaderState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A2B3C4D00000012AABBCC01 /* DemoReaderState.swift */; }; 1A2B3C4D00000014AABBCC01 /* FanrenParseTimeTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A2B3C4D00000013AABBCC01 /* FanrenParseTimeTest.swift */; }; 23BB1155EA379786DAA10A89 /* DisplayTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB49AFCCBC2BE04C82B8F286 /* DisplayTypeTests.swift */; }; + 369C9658D870DCAFC17EB7F7 /* SearchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE01DBDCB8D790832A4DE3F /* SearchTests.swift */; }; 3BC5C96D7A0ACF35F2192CC7 /* XCUIApplication+Launch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4C44287820D68ED6570F8 /* XCUIApplication+Launch.swift */; }; 4509ED928F228F43888E063D /* ReaderToolbarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BADF0A18AD034B74A482A4C1 /* ReaderToolbarTests.swift */; }; 52A6A0C9941A4B5F9B88C6B0 /* ReaderAnnotationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20BD15E5D8F04E7E9A239E14 /* ReaderAnnotationTests.swift */; }; @@ -26,7 +27,6 @@ C08FF8D030048DC5147729E9 /* ReaderOpenCloseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EDF066BA12974E6CFBE519F /* ReaderOpenCloseTests.swift */; }; DE1437A969DA1C5F0CBB047D /* Pods_ReadViewDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 792DF85CE4A3DD80D67843C7 /* Pods_ReadViewDemo.framework */; }; FEDB5937CEB858CB06E38E2D /* SettingsPanelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 201C2B482287866487EFAE66 /* SettingsPanelTests.swift */; }; - 369C9658D870DCAFC17EB7F7 /* SearchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE01DBDCB8D790832A4DE3F /* SearchTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -61,9 +61,9 @@ 8FFD606A5A1CBDCC3CA87F1C /* ReadViewDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReadViewDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9EDF066BA12974E6CFBE519F /* ReaderOpenCloseTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ReaderOpenCloseTests.swift; sourceTree = ""; }; BADF0A18AD034B74A482A4C1 /* ReaderToolbarTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ReaderToolbarTests.swift; sourceTree = ""; }; + CFE01DBDCB8D790832A4DE3F /* SearchTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SearchTests.swift; sourceTree = ""; }; DE070C1D2FBF0CC900ED065F /* ReadViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReadViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; FB49AFCCBC2BE04C82B8F286 /* DisplayTypeTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DisplayTypeTests.swift; sourceTree = ""; }; - CFE01DBDCB8D790832A4DE3F /* SearchTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SearchTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ diff --git a/ReadViewDemo/ReadViewDemo/ViewController.swift b/ReadViewDemo/ReadViewDemo/ViewController.swift index e917414..f667288 100644 --- a/ReadViewDemo/ReadViewDemo/ViewController.swift +++ b/ReadViewDemo/ReadViewDemo/ViewController.swift @@ -18,6 +18,9 @@ final class ViewController: UIViewController { let windowSize: Int? let concurrency: Int? let clearsCache: Bool + let corruptsCache: Bool + let allowsInspectableWebViews: Bool + let enablesVerboseWebLogs: Bool let searchKeyword: String? nonisolated private static func parseDisplayType(_ rawValue: String) -> RDReaderView.DisplayType? { @@ -57,6 +60,9 @@ final class ViewController: UIViewController { let windowSize = value(after: "--demo-window-size").flatMap(Int.init) let concurrency = value(after: "--demo-concurrency").flatMap(Int.init) let clearsCache = arguments.contains("--demo-clear-cache") + let corruptsCache = arguments.contains("--demo-corrupt-cache") + let allowsInspectableWebViews = arguments.contains("--demo-allow-inspectable-webviews") + let enablesVerboseWebLogs = arguments.contains("--demo-enable-verbose-weblogs") let searchKeyword = value(after: "--demo-search-keyword") return LaunchAutomationPlan( @@ -69,6 +75,9 @@ final class ViewController: UIViewController { windowSize: windowSize, concurrency: concurrency, clearsCache: clearsCache, + corruptsCache: corruptsCache, + allowsInspectableWebViews: allowsInspectableWebViews, + enablesVerboseWebLogs: enablesVerboseWebLogs, searchKeyword: searchKeyword ) } @@ -262,6 +271,9 @@ final class ViewController: UIViewController { if launchAutomationPlan.clearsCache { clearChapterSummaryCache() } + if launchAutomationPlan.corruptsCache { + corruptChapterSummaryCache() + } var configuration = RDEPUBReaderConfiguration.default if let displayType = launchAutomationPlan.displayType { @@ -273,6 +285,8 @@ final class ViewController: UIViewController { if let concurrency = launchAutomationPlan.concurrency { configuration.metadataParsingConcurrency = concurrency } + configuration.allowsInspectableWebViews = launchAutomationPlan.allowsInspectableWebViews + configuration.enablesVerboseWebViewLogging = launchAutomationPlan.enablesVerboseWebLogs _ = openBook(book, configuration: configuration, automationPlan: launchAutomationPlan) } @@ -300,6 +314,19 @@ final class ViewController: UIViewController { print("[ReadViewDemo] cleared chapter summary cache at \(cacheDir.path)") } + private func corruptChapterSummaryCache() { + let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first + ?? FileManager.default.temporaryDirectory + let cacheDir = cachesDirectory.appendingPathComponent("RDEPUBChapterSummaryCache", isDirectory: true) + guard let enumerator = FileManager.default.enumerator(at: cacheDir, includingPropertiesForKeys: nil) else { + return + } + for case let fileURL as URL in enumerator where fileURL.pathExtension == "json" { + try? Data("corrupted-cache".utf8).write(to: fileURL, options: .atomic) + } + print("[ReadViewDemo] corrupted chapter summary cache at \(cacheDir.path)") + } + } extension ViewController: UITableViewDataSource { diff --git a/ReadViewDemo/ReadViewDemo/book/Web交互流式外链验证.epub b/ReadViewDemo/ReadViewDemo/book/Web交互流式外链验证.epub new file mode 100644 index 0000000..71d29f2 Binary files /dev/null and b/ReadViewDemo/ReadViewDemo/book/Web交互流式外链验证.epub differ diff --git a/ReadViewDemo/ReadViewDemo/book/损坏结构样本.epub b/ReadViewDemo/ReadViewDemo/book/损坏结构样本.epub new file mode 100644 index 0000000..712a3cb --- /dev/null +++ b/ReadViewDemo/ReadViewDemo/book/损坏结构样本.epub @@ -0,0 +1 @@ +not a valid epub archive \ No newline at end of file diff --git a/ReadViewDemo/ReadViewDemo/book/空归档样本.epub b/ReadViewDemo/ReadViewDemo/book/空归档样本.epub new file mode 100644 index 0000000..e69de29 diff --git a/ReadViewDemo/ReadViewDemo/book/缺失文件样本.epub b/ReadViewDemo/ReadViewDemo/book/缺失文件样本.epub new file mode 100644 index 0000000..0102d8a Binary files /dev/null and b/ReadViewDemo/ReadViewDemo/book/缺失文件样本.epub differ diff --git a/ReadViewDemo/ReadViewDemoUITests/Helpers/DemoReaderState.swift b/ReadViewDemo/ReadViewDemoUITests/Helpers/DemoReaderState.swift index ab06e50..1bcd559 100644 --- a/ReadViewDemo/ReadViewDemoUITests/Helpers/DemoReaderState.swift +++ b/ReadViewDemo/ReadViewDemoUITests/Helpers/DemoReaderState.swift @@ -40,6 +40,16 @@ struct DemoReaderState { var windowSize: Int? { fields["windowSize"].flatMap(Int.init) } var parseMs: Int? { fields["parseMs"].flatMap(Int.init) } var parseConcurrency: Int? { fields["parseConcurrency"].flatMap(Int.init) } + var inspectable: Int? { fields["inspectable"].flatMap(Int.init) } + var streamedResources: Int? { fields["streamedResources"].flatMap(Int.init) } + var inMemoryResources: Int? { fields["inMemoryResources"].flatMap(Int.init) } + var resourceFailures: Int? { fields["resourceFailures"].flatMap(Int.init) } + var cacheFiles: Int? { fields["cacheFiles"].flatMap(Int.init) } + var cacheBytes: Int? { fields["cacheBytes"].flatMap(Int.init) } + var externalLinks: Int? { fields["externalLinks"].flatMap(Int.init) } + var lastExternalURL: String? { fields["lastExternalURL"] } + var lastError: String? { fields["lastError"] } + var searchMatchText: String? { fields["searchMatchText"] } } extension XCUIApplication { diff --git a/ReadViewDemo/ReadViewDemoUITests/Helpers/XCUIApplication+Launch.swift b/ReadViewDemo/ReadViewDemoUITests/Helpers/XCUIApplication+Launch.swift index 08f23b4..b3a18ef 100644 --- a/ReadViewDemo/ReadViewDemoUITests/Helpers/XCUIApplication+Launch.swift +++ b/ReadViewDemo/ReadViewDemoUITests/Helpers/XCUIApplication+Launch.swift @@ -9,6 +9,9 @@ extension XCUIApplication { windowSize: Int? = nil, concurrency: Int? = nil, clearsCache: Bool = false, + corruptsCache: Bool = false, + allowsInspectableWebViews: Bool = false, + enablesVerboseWebLogs: Bool = false, searchKeyword: String? = nil ) { var args = ["--demo-book-title", bookTitleQuery] @@ -18,6 +21,15 @@ extension XCUIApplication { if clearsCache { args.append("--demo-clear-cache") } + if corruptsCache { + args.append("--demo-corrupt-cache") + } + if allowsInspectableWebViews { + args.append("--demo-allow-inspectable-webviews") + } + if enablesVerboseWebLogs { + args.append("--demo-enable-verbose-weblogs") + } if let displayType { args += ["--demo-display-type", displayType] } @@ -52,16 +64,27 @@ extension XCUIApplication { } } - func showReaderChromeIfNeeded() { - if buttons[IDs.readerBack].exists && buttons[IDs.readerSettings].exists { + func showReaderChromeIfNeeded( + requiredButtonIDs: [String] = [IDs.readerBack, IDs.readerSettings], + timeout: TimeInterval = 5 + ) { + if readerChromeIsVisible(requiredButtonIDs: requiredButtonIDs) { return } - let content = otherElements[IDs.readerContent] - if content.waitForExistence(timeout: 3) { - content.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() - } else { - windows.firstMatch.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() + let deadline = Date().addingTimeInterval(timeout) + while Date() < deadline { + let content = otherElements[IDs.readerContent] + if content.waitForExistence(timeout: 1) { + content.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() + } else { + windows.firstMatch.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() + } + + RunLoop.current.run(until: Date().addingTimeInterval(0.35)) + if readerChromeIsVisible(requiredButtonIDs: requiredButtonIDs) { + return + } } } @@ -94,4 +117,39 @@ extension XCUIApplication { XCTFail("阅读器状态未包含 \(expectedText),当前:\(lastLabel)") } + + @discardableResult + func requireSelectionText( + expectedPage: Int? = nil, + timeout: TimeInterval = 12 + ) throws -> XCUIElement { + let selectionText = otherElements[IDs.readerSelectionText].firstMatch + let deadline = Date().addingTimeInterval(timeout) + + while Date() < deadline { + if let expectedPage { + let state = currentDemoReaderState() + if state?.page != expectedPage { + RunLoop.current.run(until: Date().addingTimeInterval(0.1)) + continue + } + } + + if selectionText.exists { + return selectionText + } + + let content = otherElements[IDs.readerContent] + if content.exists { + content.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() + } + RunLoop.current.run(until: Date().addingTimeInterval(0.2)) + } + + throw XCTSkip("文本选区元素未出现,当前渲染路径未暴露 \(IDs.readerSelectionText)") + } + + private func readerChromeIsVisible(requiredButtonIDs: [String]) -> Bool { + requiredButtonIDs.allSatisfy { buttons[$0].exists } + } } diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/BookmarkChromeStateTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/BookmarkChromeStateTests.swift new file mode 100644 index 0000000..399a446 --- /dev/null +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/BookmarkChromeStateTests.swift @@ -0,0 +1,74 @@ +import XCTest + +/// 书签 chrome 状态测试 +/// 覆盖:书签列表按钮在零书签时禁用、添加后启用、书签跨重启存活 +final class BookmarkChromeStateTests: XCTestCase { + + private let app = XCUIApplication() + + override func setUpWithError() throws { + continueAfterFailure = false + } + + /// 零书签时,底部书签列表按钮应禁用 + func testBookmarksListButtonDisabledWhenNoBookmarks() throws { + app.launchAndOpenSampleBook() + app.waitForReader() + + app.showReaderChromeIfNeeded() + + let bookmarksButton = app.buttons[IDs.readerBookmarks] + guard bookmarksButton.waitForExistence(timeout: 3) else { + throw XCTSkip("书签列表按钮不存在") + } + // 零书签时按钮应存在但不可用 + XCTAssertFalse(bookmarksButton.isEnabled, "零书签时书签列表按钮应禁用") + } + + /// 添加书签后,底部书签列表按钮应启用 + func testBookmarksListButtonEnabledAfterAdding() throws { + app.launchAndOpenSampleBook() + app.waitForReader() + + app.showReaderChromeIfNeeded() + + // 添加书签 + let bookmarkButton = app.buttons[IDs.readerBookmark] + guard bookmarkButton.waitForExistence(timeout: 3) else { + throw XCTSkip("书签按钮不存在") + } + bookmarkButton.tap() + + // 验证书签列表按钮启用 + let bookmarksButton = app.buttons[IDs.readerBookmarks] + guard bookmarksButton.waitForExistence(timeout: 3) else { + throw XCTSkip("书签列表按钮不存在") + } + XCTAssertTrue(bookmarksButton.isEnabled, "添加书签后书签列表按钮应启用") + } + + /// 书签跨重启存活 + func testBookmarkSurvivesRestart() { + app.launchAndOpenSampleBook() + app.waitForReader() + + // 添加书签 + app.showReaderChromeIfNeeded() + let bookmarkButton = app.buttons[IDs.readerBookmark] + bookmarkButton.waitForExistence(timeout: 3) + bookmarkButton.tap() + + // 等待书签状态确认 + app.waitForDemoReaderState(timeout: 5, description: "bookmarks=1") { $0.bookmarks == 1 } + + // 关闭阅读器 + app.buttons[IDs.readerBack].tap() + + // 重新打开(不重置状态) + app.launchAndOpenSampleBook(resetsReaderState: false) + app.waitForReader() + + // 验证书签仍然存在 + app.waitForDemoReaderState(timeout: 5, description: "bookmarks=1 after restart") { $0.bookmarks == 1 } + } +} diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/BookmarkManagementTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/BookmarkManagementTests.swift index 74db5d7..75a65ee 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/BookmarkManagementTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/BookmarkManagementTests.swift @@ -15,7 +15,7 @@ final class BookmarkManagementTests: XCTestCase { let bookmarkButton = app.buttons[IDs.readerBookmark] XCTAssertTrue(bookmarkButton.waitForExistence(timeout: 3), "书签按钮不存在") bookmarkButton.tap() - app.waitForReaderState(containing: "bookmarks=1", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "bookmarks=1") { $0.bookmarks == 1 } app.showReaderChromeIfNeeded() let bookmarksButton = app.buttons[IDs.readerBookmarks] @@ -47,7 +47,7 @@ final class BookmarkManagementTests: XCTestCase { app.showReaderChromeIfNeeded() app.buttons[IDs.readerBookmark].tap() - app.waitForReaderState(containing: "bookmarks=1", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "bookmarks=1") { $0.bookmarks == 1 } app.showReaderChromeIfNeeded() app.buttons[IDs.readerBookmarks].tap() @@ -78,6 +78,39 @@ final class BookmarkManagementTests: XCTestCase { XCTAssertTrue(bookmarkButton.waitForExistence(timeout: 3), "移除书签后按钮应仍然存在") } + func testBookmarksButtonDisabledWhenNoBookmarks() throws { + app.launchAndOpenSampleBook(resetsReaderState: true) + app.waitForReader(timeout: 12) + + app.showReaderChromeIfNeeded() + let bookmarksButton = app.buttons[IDs.readerBookmarks] + XCTAssertTrue(bookmarksButton.waitForExistence(timeout: 3), "书签列表按钮不存在") + XCTAssertFalse(bookmarksButton.isEnabled, "零书签时书签列表按钮应禁用") + } + + func testBookmarkIndicatorStaysSelectedAtBookmarkedLocation() throws { + app.launchAndOpenSampleBook(resetsReaderState: true) + app.waitForReader(timeout: 12) + + app.showReaderChromeIfNeeded() + let bookmarkButton = app.buttons[IDs.readerBookmark] + XCTAssertTrue(bookmarkButton.waitForExistence(timeout: 3), "书签按钮不存在") + bookmarkButton.tap() + app.waitForDemoReaderState(timeout: 5, description: "bookmarks=1") { $0.bookmarks == 1 } + XCTAssertEqual(bookmarkButton.value as? String, "selected", "当前页已加书签时顶部书签按钮应为选中态") + + let paging = app.collectionViews[IDs.readerPaging] + if paging.waitForExistence(timeout: 5) { + paging.swipeLeft() + app.showReaderChromeIfNeeded() + XCTAssertEqual(bookmarkButton.value as? String, "unselected", "离开书签页后顶部书签按钮应取消选中") + + paging.swipeRight() + app.showReaderChromeIfNeeded() + XCTAssertEqual(bookmarkButton.value as? String, "selected", "返回书签页后顶部书签按钮应恢复选中") + } + } + private func waitForCellCount( in table: XCUIElement, minimum: Int, diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/DisplayTypeTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/DisplayTypeTests.swift index 009d946..93bd95e 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/DisplayTypeTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/DisplayTypeTests.swift @@ -15,12 +15,37 @@ final class DisplayTypeTests: XCTestCase { func testOpenWithHorizontalScroll() { app.launchAndOpenSampleBook(displayType: "scroll") app.waitForReader() - app.waitForReaderState(containing: "display=horizontalScroll") + app.waitForDemoReaderState(timeout: 8, description: "display=horizontalScroll") { $0.display == "horizontalScroll" } } func testOpenWithVerticalScroll() { app.launchAndOpenSampleBook(displayType: "vertical") app.waitForReader() - app.waitForReaderState(containing: "display=verticalScroll") + app.waitForDemoReaderState(timeout: 8, description: "display=verticalScroll") { $0.display == "verticalScroll" } + } + + func testInspectableWebViewsDisabledByDefault() { + app.launchAndOpenSampleBook(bookTitleQuery: "Web交互流式外链验证") + app.waitForReader() + + let state = app.waitForDemoReaderState(timeout: 8, description: "inspectable=0") { $0.inspectable != nil } + XCTAssertEqual(state.inspectable, 0, "默认配置下 WebView inspectable 应关闭") + } + + func testDisplayTypeSwitchMidSession() { + app.launchAndOpenSampleBook(displayType: "scroll") + app.waitForReader() + app.waitForDemoReaderState(timeout: 8, description: "display=horizontalScroll") { $0.display == "horizontalScroll" } + + app.showReaderChromeIfNeeded() + XCTAssertTrue(app.buttons[IDs.readerSettings].waitForExistence(timeout: 3)) + app.buttons[IDs.readerSettings].tap() + + let displayTypeControl = app.segmentedControls[IDs.settingsDisplayType] + XCTAssertTrue(displayTypeControl.waitForExistence(timeout: 5)) + displayTypeControl.buttons.element(boundBy: 2).tap() + app.buttons[IDs.settingsDone].tap() + + app.waitForDemoReaderState(timeout: 8, description: "display=verticalScroll after switch") { $0.display == "verticalScroll" } } } diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ErrorAndEdgeCaseTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ErrorAndEdgeCaseTests.swift index 268863a..4d32826 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ErrorAndEdgeCaseTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ErrorAndEdgeCaseTests.swift @@ -14,7 +14,7 @@ final class ErrorAndEdgeCaseTests: XCTestCase { let paging = app.collectionViews[IDs.readerPaging] if paging.waitForExistence(timeout: 5) { paging.swipeUp() } - app.waitForReaderState(containing: "reader=opened", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "reader=opened") { $0.isOpened } } func testOpenAndCloseMultipleTimes() throws { @@ -42,7 +42,7 @@ final class ErrorAndEdgeCaseTests: XCTestCase { for _ in 1...5 { paging.swipeLeft() } - app.waitForReaderState(containing: "reader=opened", timeout: 8) + app.waitForDemoReaderState(timeout: 8, description: "reader=opened") { $0.isOpened } } func testSettingsPanelScrollReachesAllControls() throws { @@ -66,4 +66,85 @@ final class ErrorAndEdgeCaseTests: XCTestCase { app.buttons[IDs.settingsDone].tap() } -} \ No newline at end of file + + func testHTTPSExternalLinkShowsConfirmationAlert() throws { + app.launchAndOpenSampleBook(bookTitleQuery: "Web交互流式外链验证", displayType: "scroll") + app.waitForReader(timeout: 20) + + let httpsLink = externalLinkElement(named: "HTTPS Link") + guard httpsLink.waitForExistence(timeout: 8) else { + throw XCTSkip("HTTPS 外链元素未暴露") + } + httpsLink.tap() + + XCTAssertTrue(app.alerts["打开外部链接"].waitForExistence(timeout: 5), "HTTPS 外链应弹出确认框") + XCTAssertTrue(app.buttons["打开"].waitForExistence(timeout: 2), "确认框应提供打开按钮") + app.buttons["取消"].tap() + } + + func testMailtoExternalLinkBlockedByDefaultPolicy() throws { + app.launchAndOpenSampleBook(bookTitleQuery: "Web交互流式外链验证", displayType: "scroll") + app.waitForReader(timeout: 20) + + let mailtoLink = externalLinkElement(named: "Mailto Link") + guard mailtoLink.waitForExistence(timeout: 8) else { + throw XCTSkip("Mailto 外链元素未暴露") + } + mailtoLink.tap() + + XCTAssertFalse(app.alerts["打开外部链接"].waitForExistence(timeout: 2), "默认策略下 mailto 不应弹出确认框") + let state = app.waitForDemoReaderState(timeout: 5, description: "mailto activation recorded") { state in + (state.externalLinks ?? 0) >= 1 && state.lastExternalURL != nil + } + XCTAssertTrue(state.lastExternalURL?.contains("mailto:test@example.com") == true, "应记录 mailto 激活但不继续打开") + } + + func testLargeWebResourceUsesStreamingPath() throws { + app.launchAndOpenSampleBook(bookTitleQuery: "Web交互流式外链验证", displayType: "scroll") + app.waitForReader(timeout: 20) + + let state = app.waitForDemoReaderState(timeout: 15, description: "streamedResources>0") { state in + (state.streamedResources ?? 0) > 0 + } + XCTAssertGreaterThan(state.streamedResources ?? 0, 0, "大资源应走流式传输路径") + } + + func testInvalidEPUBReportsFailure() { + app.launchAndOpenSampleBook(bookTitleQuery: "损坏结构样本", resetsReaderState: true) + + let state = app.waitForDemoReaderState(timeout: 12, description: "invalid epub failure") { state in + state.lastError != nil && state.lastError != "none" + } + XCTAssertNotEqual(state.lastError, "none") + } + + func testEmptyArchiveReportsFailure() { + app.launchAndOpenSampleBook(bookTitleQuery: "空归档样本", resetsReaderState: true) + + let state = app.waitForDemoReaderState(timeout: 12, description: "empty archive failure") { state in + state.lastError != nil && state.lastError != "none" + } + XCTAssertNotEqual(state.lastError, "none") + } + + func testMissingSpineDocumentReportsFailure() { + app.launchAndOpenSampleBook(bookTitleQuery: "缺失文件样本", resetsReaderState: true) + + let state = app.waitForDemoReaderState(timeout: 12, description: "missing file failure") { state in + state.lastError != nil && state.lastError != "none" + } + XCTAssertNotEqual(state.lastError, "none") + } + + private func externalLinkElement(named label: String) -> XCUIElement { + let link = app.links[label] + if link.exists { + return link + } + let staticText = app.staticTexts[label] + if staticText.exists { + return staticText + } + return link + } +} diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/HighlightsManagementTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/HighlightsManagementTests.swift index 3480e6e..07e3651 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/HighlightsManagementTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/HighlightsManagementTests.swift @@ -23,7 +23,7 @@ final class HighlightsManagementTests: XCTestCase { if highlightMenuItem.waitForExistence(timeout: 3) { highlightMenuItem.tap() } else if app.buttons["高亮"].waitForExistence(timeout: 2) { app.buttons["高亮"].tap() } - app.waitForReaderState(containing: "highlights=", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "highlights exists") { $0.highlights != nil } app.showReaderChromeIfNeeded() XCTAssertTrue(app.buttons[IDs.readerHighlights].waitForExistence(timeout: 3), "高亮列表按钮不存在") diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/LargeBookOnDemandTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/LargeBookOnDemandTests.swift index 8721191..fedbc40 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/LargeBookOnDemandTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/LargeBookOnDemandTests.swift @@ -50,9 +50,10 @@ final class LargeBookOnDemandTests: XCTestCase { app.launchAndOpenSampleBook(bookTitleQuery: largeBookQuery, resetsReaderState: true) app.waitForReader(timeout: 20) - _ = app.waitForDemoReaderState(timeout: 60, description: "首次打开完成全书缓存") { state in + let firstFullState = app.waitForDemoReaderState(timeout: 60, description: "首次打开完成全书缓存") { state in state.mode == "bookPageMap" && state.pagination == "full" && (state.buildableChapters ?? 0) > 0 } + XCTAssertGreaterThan(firstFullState.cacheFiles ?? 0, 0, "首次全量解析后应生成章节摘要缓存") app.showReaderChromeIfNeeded() XCTAssertTrue(app.buttons[IDs.readerBack].waitForExistence(timeout: 5), "返回按钮不存在") @@ -68,6 +69,47 @@ final class LargeBookOnDemandTests: XCTestCase { XCTAssertEqual(reopenedState.pagination, "full") XCTAssertEqual(reopenedState.knownChapters, reopenedState.buildableChapters) + XCTAssertEqual(reopenedState.knownPages, firstFullState.knownPages, "二开命中缓存后总页数应稳定一致") + } + + func testCorruptedCacheFallsBackGracefully() throws { + app.launchAndOpenSampleBook(bookTitleQuery: largeBookQuery, resetsReaderState: true, clearsCache: true) + app.waitForReader(timeout: 20) + _ = app.waitForDemoReaderState(timeout: 60, description: "首次打开完成缓存建立") { state in + state.pagination == "full" && (state.cacheFiles ?? 0) > 0 + } + + app.showReaderChromeIfNeeded() + app.buttons[IDs.readerBack].tap() + XCTAssertTrue(app.tables[IDs.demoBooksTable].waitForExistence(timeout: 5)) + + app.launchAndOpenSampleBook(bookTitleQuery: largeBookQuery, resetsReaderState: false, corruptsCache: true) + app.waitForReader(timeout: 20) + + let recoveredState = app.waitForDemoReaderState(timeout: 45, description: "损坏缓存后仍可恢复阅读") { state in + state.isOpened && state.pagination != "none" && state.mode == "bookPageMap" + } + XCTAssertEqual(recoveredState.lastError, "none", "缓存损坏不应导致阅读器进入错误状态") + } + + func testClearingCacheForcesRebuild() throws { + app.launchAndOpenSampleBook(bookTitleQuery: largeBookQuery, resetsReaderState: true, clearsCache: true) + app.waitForReader(timeout: 20) + _ = app.waitForDemoReaderState(timeout: 60, description: "首次缓存建立") { state in + state.pagination == "full" && (state.cacheFiles ?? 0) > 0 + } + + app.showReaderChromeIfNeeded() + app.buttons[IDs.readerBack].tap() + XCTAssertTrue(app.tables[IDs.demoBooksTable].waitForExistence(timeout: 5)) + + app.launchAndOpenSampleBook(bookTitleQuery: largeBookQuery, resetsReaderState: false, clearsCache: true) + app.waitForReader(timeout: 20) + + let rebuiltState = app.waitForDemoReaderState(timeout: 60, description: "清空缓存后重新构建") { state in + state.pagination == "full" && (state.cacheFiles ?? 0) > 0 + } + XCTAssertGreaterThan(rebuiltState.cacheFiles ?? 0, 0, "清空缓存后应重新写入缓存文件") } func testMiddleChapterOpenPositionStableAfterFullParse() throws { @@ -146,7 +188,7 @@ final class LargeBookOnDemandTests: XCTestCase { func testLargeBookContinuousPagingExtendsKnownPageMap() throws { app.launchAndOpenSampleBook(bookTitleQuery: largeBookQuery, displayType: "scroll", resetsReaderState: true) app.waitForReader(timeout: 20) - app.waitForReaderState(containing: "display=horizontalScroll", timeout: 8) + app.waitForDemoReaderState(timeout: 8, description: "display=horizontalScroll") { $0.display == "horizontalScroll" } let initialState = app.waitForDemoReaderState(timeout: 15, description: "初始局部分页") { state in state.mode == "bookPageMap" && state.knownPages != nil && state.page != nil diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/LocationPersistenceTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/LocationPersistenceTests.swift index b1c9bb8..6b0cb7d 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/LocationPersistenceTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/LocationPersistenceTests.swift @@ -57,7 +57,7 @@ final class LocationPersistenceTests: XCTestCase { let paging = app.collectionViews[IDs.readerPaging] if paging.waitForExistence(timeout: 5) { paging.swipeLeft() } - app.waitForReaderState(containing: "page=", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "page exists") { $0.page != nil } app.showReaderChromeIfNeeded() if app.buttons[IDs.readerSettings].waitForExistence(timeout: 3) { app.buttons[IDs.readerSettings].tap() } @@ -70,6 +70,37 @@ final class LocationPersistenceTests: XCTestCase { app.launchAndOpenSampleBook() app.waitForReader(timeout: 12) - app.waitForReaderState(containing: "reader=opened", timeout: 8) + app.waitForDemoReaderState(timeout: 8, description: "reader=opened") { $0.isOpened } + } + + func testBookmarkAndHighlightPersistAcrossReopen() throws { + app.launchAndOpenSampleBook(resetsReaderState: true) + app.waitForReader(timeout: 12) + + app.showReaderChromeIfNeeded() + XCTAssertTrue(app.buttons[IDs.readerBookmark].waitForExistence(timeout: 3)) + app.buttons[IDs.readerBookmark].tap() + app.waitForDemoReaderState(timeout: 5, description: "bookmarks=1") { $0.bookmarks == 1 } + + app.hideReaderChromeIfNeeded() + let content = app.otherElements[IDs.readerSelectionText].firstMatch + XCTAssertTrue(content.waitForExistence(timeout: 5)) + let start = content.coordinate(withNormalizedOffset: CGVector(dx: 0.2, dy: 0.5)) + let end = content.coordinate(withNormalizedOffset: CGVector(dx: 0.55, dy: 0.5)) + start.press(forDuration: 0.8, thenDragTo: end) + XCTAssertTrue(app.menuItems["高亮"].waitForExistence(timeout: 3)) + app.menuItems["高亮"].tap() + app.waitForDemoReaderState(timeout: 8, description: "highlights=1") { $0.highlights == 1 } + + app.showReaderChromeIfNeeded() + app.buttons[IDs.readerBack].tap() + + app.launchAndOpenSampleBook(resetsReaderState: false) + app.waitForReader(timeout: 12) + let state = app.waitForDemoReaderState(timeout: 8, description: "bookmark/highlight restored") { state in + state.bookmarks == 1 && state.highlights == 1 + } + XCTAssertEqual(state.bookmarks, 1) + XCTAssertEqual(state.highlights, 1) } } diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/NavigationBackwardTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/NavigationBackwardTests.swift new file mode 100644 index 0000000..d88afd0 --- /dev/null +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/NavigationBackwardTests.swift @@ -0,0 +1,83 @@ +import XCTest + +/// 向后导航和显示模式切换测试 +/// 覆盖:向右滑动回到上一页、阅读中途切换显示模式 +final class NavigationBackwardTests: XCTestCase { + + private let app = XCUIApplication() + + override func setUpWithError() throws { + continueAfterFailure = false + } + + /// 向右滑动应返回上一页 + func testSwipeRightGoesToPreviousPage() throws { + app.launchAndOpenSampleBook() + app.waitForReader() + + // 先向左滑动两次前进 + let content = app.otherElements[IDs.readerContent].firstMatch + guard content.waitForExistence(timeout: 5) else { + throw XCTSkip("内容区域不存在") + } + + content.swipeLeft() + Thread.sleep(forTimeInterval: 0.5) + content.swipeLeft() + Thread.sleep(forTimeInterval: 0.5) + + // 记录当前页码 + let stateAfterForward = app.waitForDemoReaderState(timeout: 5, description: "page after forward") { + ($0.page ?? 0) > 1 + } + let pageAfterForward = stateAfterForward.page ?? 2 + XCTAssertGreaterThan(pageAfterForward, 1, "应已前进到第 2 页或更后") + + // 向右滑动返回 + content.swipeRight() + Thread.sleep(forTimeInterval: 0.5) + + // 验证页码减少 + let stateAfterBack = app.waitForDemoReaderState(timeout: 5, description: "page after back") { + ($0.page ?? 0) < pageAfterForward + } + XCTAssertLessThan(stateAfterBack.page ?? 0, pageAfterForward, "向右滑动后页码应减少") + } + + /// 阅读中途切换显示模式 + func testDisplayTypeSwitchMidSession() throws { + app.launchAndOpenSampleBook(displayType: "horizontalScroll") + app.waitForReader() + + // 确认初始模式 + app.waitForDemoReaderState(timeout: 5, description: "initial horizontalScroll") { + $0.display == "horizontalScroll" + } + + // 打开设置面板 + app.showReaderChromeIfNeeded() + let settingsButton = app.buttons[IDs.readerSettings] + guard settingsButton.waitForExistence(timeout: 3) else { + throw XCTSkip("设置按钮不存在") + } + settingsButton.tap() + + // 切换到垂直滚动 + let displayTypeButton = app.buttons[IDs.settingsDisplayType] + guard displayTypeButton.waitForExistence(timeout: 3) else { + throw XCTSkip("显示模式按钮不存在") + } + displayTypeButton.tap() + + // 关闭设置 + let doneButton = app.buttons[IDs.settingsDone] + if doneButton.waitForExistence(timeout: 3) { + doneButton.tap() + } + + // 验证显示模式已切换 + app.waitForDemoReaderState(timeout: 5, description: "switched to verticalScroll") { + $0.display == "verticalScroll" + } + } +} diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/PageNavigationTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/PageNavigationTests.swift index e17f0ed..2600619 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/PageNavigationTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/PageNavigationTests.swift @@ -18,7 +18,7 @@ final class PageNavigationTests: XCTestCase { func testPagingViewExistsInScrollMode() { app.launchAndOpenSampleBook(displayType: "scroll") app.waitForReader() - app.waitForReaderState(containing: "display=horizontalScroll", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "display=horizontalScroll") { $0.display == "horizontalScroll" } let paging = app.collectionViews[IDs.readerPaging] XCTAssertTrue(paging.waitForExistence(timeout: 5), "分页滚动视图未出现") @@ -27,7 +27,7 @@ final class PageNavigationTests: XCTestCase { func testPagingViewExistsInVerticalScrollMode() { app.launchAndOpenSampleBook(displayType: "vertical") app.waitForReader() - app.waitForReaderState(containing: "display=verticalScroll", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "display=verticalScroll") { $0.display == "verticalScroll" } let paging = app.collectionViews[IDs.readerPaging] XCTAssertTrue(paging.waitForExistence(timeout: 5), "分页滚动视图未出现") @@ -36,7 +36,7 @@ final class PageNavigationTests: XCTestCase { func testSwipeInHorizontalScrollMode() { app.launchAndOpenSampleBook(displayType: "scroll") app.waitForReader() - app.waitForReaderState(containing: "display=horizontalScroll", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "display=horizontalScroll") { $0.display == "horizontalScroll" } let paging = app.collectionViews[IDs.readerPaging] XCTAssertTrue(paging.waitForExistence(timeout: 5)) @@ -51,4 +51,26 @@ final class PageNavigationTests: XCTestCase { } XCTAssertNotEqual(beforePage, afterState.page, "水平滑动后页码未变化") } + + func testSwipeRightReturnsToPreviousPage() { + app.launchAndOpenSampleBook(displayType: "scroll", pageNumber: 3) + app.waitForReader() + app.waitForDemoReaderState(timeout: 8, description: "page=3") { $0.page == 3 } + + let paging = app.collectionViews[IDs.readerPaging] + XCTAssertTrue(paging.waitForExistence(timeout: 5)) + paging.swipeRight() + + let state = app.waitForDemoReaderState(timeout: 8, description: "returned to previous page") { state in + guard let page = state.page else { return false } + return page < 3 + } + XCTAssertEqual(state.page, 2, "向右滑动后应回到上一页") + } + + func testLaunchAtSpecificPageNumber() { + app.launchAndOpenSampleBook(pageNumber: 4) + app.waitForReader() + app.waitForDemoReaderState(timeout: 8, description: "page=4") { $0.page == 4 } + } } diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ReaderAnnotationExtendedTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ReaderAnnotationExtendedTests.swift index f26a3c7..7fa003e 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ReaderAnnotationExtendedTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ReaderAnnotationExtendedTests.swift @@ -37,12 +37,11 @@ final class ReaderAnnotationExtendedTests: XCTestCase { XCTAssertTrue(app.buttons[IDs.readerSettings].exists, "设置按钮未出现") } - func testSelectionTextElementExists() { + func testSelectionTextElementExists() throws { app.launchAndOpenSampleBook() app.waitForReader() - let selectionText = app.otherElements[IDs.readerSelectionText].firstMatch - XCTAssertTrue(selectionText.waitForExistence(timeout: 5), "文本选区元素未出现") + _ = try app.requireSelectionText() } func testReaderStateReportsPageInfo() { diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ReaderAnnotationTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ReaderAnnotationTests.swift index 80a9b83..a4936c1 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ReaderAnnotationTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ReaderAnnotationTests.swift @@ -7,22 +7,20 @@ final class ReaderAnnotationTests: XCTestCase { continueAfterFailure = false } - func testSelectionMenuCreatesHighlight() { - app.launchAndOpenSampleBook(pageNumber: 2) + func testSelectionMenuCreatesHighlight() throws { + app.launchAndOpenSampleBook() app.waitForReader() - app.waitForReaderPage(2) app.hideReaderChromeIfNeeded() - let content = app.otherElements[IDs.readerSelectionText].firstMatch - XCTAssertTrue(content.waitForExistence(timeout: 5), "阅读内容区域未出现") + let content = try app.requireSelectionText() - app.waitForReaderState(containing: "toolbar=hidden", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "toolbar=hidden") { $0.toolbar == "hidden" } let start = content.coordinate(withNormalizedOffset: CGVector(dx: 0.2, dy: 0.5)) let end = content.coordinate(withNormalizedOffset: CGVector(dx: 0.8, dy: 0.5)) start.press(forDuration: 0.8, thenDragTo: end) - app.waitForReaderState(containing: "selection=1", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "selection=1") { $0.selection == 1 } let highlightMenuItem = app.menuItems["高亮"] let highlightButton = app.buttons[IDs.readerSelectionHighlight] @@ -34,25 +32,43 @@ final class ReaderAnnotationTests: XCTestCase { XCTFail("选中文本后未出现高亮菜单") } - app.waitForReaderState(containing: "highlights=1", timeout: 8) + app.waitForDemoReaderState(timeout: 8, description: "highlights=1") { $0.highlights == 1 } } - func testLongPressSelectionDoesNotShowToolbars() { - app.launchAndOpenSampleBook(pageNumber: 2) + func testLongPressSelectionDoesNotShowToolbars() throws { + app.launchAndOpenSampleBook() app.waitForReader() - app.waitForReaderPage(2) app.hideReaderChromeIfNeeded() - let content = app.otherElements[IDs.readerSelectionText].firstMatch - XCTAssertTrue(content.waitForExistence(timeout: 5), "阅读内容区域未出现") + let content = try app.requireSelectionText() - app.waitForReaderState(containing: "toolbar=hidden", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "toolbar=hidden") { $0.toolbar == "hidden" } let start = content.coordinate(withNormalizedOffset: CGVector(dx: 0.2, dy: 0.5)) let end = content.coordinate(withNormalizedOffset: CGVector(dx: 0.8, dy: 0.5)) start.press(forDuration: 0.8, thenDragTo: end) - app.waitForReaderState(containing: "selection=1", timeout: 8) - app.waitForReaderState(containing: "toolbar=hidden", timeout: 3) + app.waitForDemoReaderState(timeout: 8, description: "selection=1") { $0.selection == 1 } + app.waitForDemoReaderState(timeout: 3, description: "toolbar=hidden") { $0.toolbar == "hidden" } + } + + func testSelectionMenuAnnotateCreatesHighlight() throws { + app.launchAndOpenSampleBook() + app.waitForReader() + app.hideReaderChromeIfNeeded() + + let content = try app.requireSelectionText() + + let start = content.coordinate(withNormalizedOffset: CGVector(dx: 0.25, dy: 0.5)) + let end = content.coordinate(withNormalizedOffset: CGVector(dx: 0.7, dy: 0.5)) + start.press(forDuration: 0.8, thenDragTo: end) + + app.waitForDemoReaderState(timeout: 5, description: "selection=1") { $0.selection == 1 } + + let annotateMenuItem = app.menuItems["批注"] + XCTAssertTrue(annotateMenuItem.waitForExistence(timeout: 3), "选中文本后未出现批注菜单") + annotateMenuItem.tap() + + app.waitForDemoReaderState(timeout: 8, description: "highlights=1 after annotate") { $0.highlights == 1 } } } diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SearchTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SearchTests.swift index efed5c6..1b981b6 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SearchTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SearchTests.swift @@ -247,6 +247,189 @@ final class SearchTests: XCTestCase { XCTAssertNotEqual(countLabel.label, "0/0", "搜索状态应在工具栏恢复后保留") } + // MARK: - 搜索高亮字符验证 + + /// 搜索匹配文本应与搜索关键字一致 + /// 验证当前匹配项的高亮确实覆盖了搜索的关键字 + func testSearchMatchTextEqualsKeyword() { + let keyword = "辽" + app.launchAndOpenSampleBook(bookTitleQuery: "宝山辽墓材料与释读", searchKeyword: keyword) + app.waitForReader() + app.showReaderChromeIfNeeded() + + // 等待搜索结果 + let countLabel = app.staticTexts[IDs.searchCount] + XCTAssertTrue(countLabel.waitForExistence(timeout: 10), "搜索计数应出现") + XCTAssertNotEqual(countLabel.label, "0/0", "关键词 '\(keyword)' 应有匹配") + + // 验证当前匹配的文本就是搜索关键字 + let state = app.waitForDemoReaderState(timeout: 5, description: "searchMatchText exists") { + $0.searchMatchText != nil && $0.searchMatchText != "none" + } + XCTAssertEqual(state.searchMatchText, keyword, + "当前搜索匹配文本应为 '\(keyword)',实际:\(state.searchMatchText ?? "nil")") + } + + /// 重排后搜索匹配文本仍应与关键字一致 + func testSearchMatchTextAfterRepagination() { + let keyword = "辽" + app.launchAndOpenSampleBook(bookTitleQuery: "宝山辽墓材料与释读", searchKeyword: keyword) + app.waitForReader() + app.showReaderChromeIfNeeded() + + // 等待搜索结果 + let countLabel = app.staticTexts[IDs.searchCount] + XCTAssertTrue(countLabel.waitForExistence(timeout: 10), "搜索计数应出现") + + // 改变字体触发重排 + app.showReaderChromeIfNeeded() + let settingsButton = app.buttons[IDs.readerSettings] + XCTAssertTrue(settingsButton.waitForExistence(timeout: 3)) + settingsButton.tap() + + let fontIncrease = app.buttons[IDs.settingsFontIncrease] + XCTAssertTrue(fontIncrease.waitForExistence(timeout: 3)) + fontIncrease.tap() + fontIncrease.tap() + Thread.sleep(forTimeInterval: 0.5) + + let doneButton = app.buttons[IDs.settingsDone] + if doneButton.waitForExistence(timeout: 3) { + doneButton.tap() + } + Thread.sleep(forTimeInterval: 1) + + // 重新打开搜索并导航到匹配 + app.showReaderChromeIfNeeded() + let prevButton = app.buttons[IDs.searchPrevious] + let nextButton = app.buttons[IDs.searchNext] + if prevButton.waitForExistence(timeout: 3), prevButton.isEnabled { + prevButton.tap() + Thread.sleep(forTimeInterval: 0.5) + } + if nextButton.waitForExistence(timeout: 3), nextButton.isEnabled { + nextButton.tap() + Thread.sleep(forTimeInterval: 0.5) + } + + // 验证重排后匹配文本仍正确 + let state = app.waitForDemoReaderState(timeout: 5, description: "searchMatchText after repagination") { + $0.searchMatchText != nil && $0.searchMatchText != "none" + } + XCTAssertEqual(state.searchMatchText, keyword, + "重排后搜索匹配文本应为 '\(keyword)',实际:\(state.searchMatchText ?? "nil")") + } + + // MARK: - 重排后搜索跳转正确性 + + /// 重排后搜索跳转页码应正确(多匹配覆盖) + /// 回归:chapterOffset(for:) 曾优先使用 row/column 查表而非存储的 chapterOffset, + /// 导致重新排版后搜索跳转到错误的页面 + func testSearchNavigationAfterRepagination() { + app.launchAndOpenSampleBook(searchKeyword: "的") + app.waitForReader() + app.showReaderChromeIfNeeded() + + // 等待搜索结果 + let countLabel = app.staticTexts[IDs.searchCount] + XCTAssertTrue(countLabel.waitForExistence(timeout: 10), "搜索计数应出现") + XCTAssertNotEqual(countLabel.label, "0/0", "关键词 '的' 应有匹配") + + // 解析匹配总数 + let countText = countLabel.label + let parts = countText.split(separator: "/") + guard parts.count == 2, let total = Int(parts[1]), total >= 3 else { + XCTSkip("需要至少 3 个匹配才能测试多页跳转") + return + } + + // 改变字体大小触发重新排版 + app.showReaderChromeIfNeeded() + let settingsButton = app.buttons[IDs.readerSettings] + XCTAssertTrue(settingsButton.waitForExistence(timeout: 3), "设置按钮应存在") + settingsButton.tap() + + let fontIncrease = app.buttons[IDs.settingsFontIncrease] + XCTAssertTrue(fontIncrease.waitForExistence(timeout: 3), "字号增大按钮应存在") + fontIncrease.tap() + fontIncrease.tap() + Thread.sleep(forTimeInterval: 0.5) + + let doneButton = app.buttons[IDs.settingsDone] + if doneButton.waitForExistence(timeout: 3) { + doneButton.tap() + } + Thread.sleep(forTimeInterval: 1) + + // 测试多个匹配位置的跳转正确性 + let nextButton = app.buttons[IDs.searchNext] + let prevButton = app.buttons[IDs.searchPrevious] + XCTAssertTrue(nextButton.waitForExistence(timeout: 3), "下一个按钮应存在") + XCTAssertTrue(prevButton.waitForExistence(timeout: 3), "上一个按钮应存在") + + // 导航到第 2 个匹配,记录页码 + nextButton.tap() + Thread.sleep(forTimeInterval: 0.5) + let stateMatch2 = app.waitForDemoReaderState(timeout: 5, description: "match 2 page") { + ($0.page ?? 0) > 0 + } + let pageAtMatch2 = stateMatch2.page ?? 1 + + // 翻到其他页 + let content = app.otherElements[IDs.readerContent].firstMatch + if content.waitForExistence(timeout: 3) { + for _ in 0..<4 { + content.swipeLeft() + Thread.sleep(forTimeInterval: 0.3) + } + Thread.sleep(forTimeInterval: 0.5) + } + + // 跳转到第 3 个匹配 + nextButton.tap() + Thread.sleep(forTimeInterval: 0.5) + let stateMatch3 = app.waitForDemoReaderState(timeout: 5, description: "match 3 page") { + ($0.page ?? 0) > 0 + } + let pageAtMatch3 = stateMatch3.page ?? 1 + + // 再翻到其他页 + if content.waitForExistence(timeout: 3) { + for _ in 0..<4 { + content.swipeLeft() + Thread.sleep(forTimeInterval: 0.3) + } + Thread.sleep(forTimeInterval: 0.5) + } + + // 跳回第 2 个匹配,验证页码不变 + prevButton.tap() + Thread.sleep(forTimeInterval: 0.5) + let stateBack2 = app.waitForDemoReaderState(timeout: 5, description: "back to match 2") { + $0.page == pageAtMatch2 + } + XCTAssertEqual(stateBack2.page, pageAtMatch2, + "重排后搜索跳回第 2 个匹配应回到第 \(pageAtMatch2) 页,实际:\(stateBack2.page ?? -1)") + + // 再翻到其他页 + if content.waitForExistence(timeout: 3) { + for _ in 0..<4 { + content.swipeLeft() + Thread.sleep(forTimeInterval: 0.3) + } + Thread.sleep(forTimeInterval: 0.5) + } + + // 跳到第 3 个匹配,验证页码不变 + nextButton.tap() + Thread.sleep(forTimeInterval: 0.5) + let stateBack3 = app.waitForDemoReaderState(timeout: 5, description: "back to match 3") { + $0.page == pageAtMatch3 + } + XCTAssertEqual(stateBack3.page, pageAtMatch3, + "重排后搜索跳到第 3 个匹配应回到第 \(pageAtMatch3) 页,实际:\(stateBack3.page ?? -1)") + } + // MARK: - 辅助方法 private func openSearchBar() { diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SelectionAnnotateTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SelectionAnnotateTests.swift new file mode 100644 index 0000000..a468543 --- /dev/null +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SelectionAnnotateTests.swift @@ -0,0 +1,162 @@ +import XCTest + +/// 选区批注动作和高亮持久化测试 +/// 覆盖:批注菜单动作创建高亮、高亮跨重启存活、重排后高亮跳转页码正确 +final class SelectionAnnotateTests: XCTestCase { + + private let app = XCUIApplication() + + override func setUpWithError() throws { + continueAfterFailure = false + } + + /// 点击批注菜单项应创建高亮 + func testAnnotateMenuActionCreatesHighlight() throws { + app.launchAndOpenSampleBook() + app.waitForReader() + + // 长按并拖拽选中文本 + let selectionText = app.otherElements[IDs.readerSelectionText].firstMatch + guard selectionText.waitForExistence(timeout: 5) else { + throw XCTSkip("文本选区元素不存在") + } + + let start = selectionText.coordinate(withNormalizedOffset: CGVector(dx: 0.3, dy: 0.5)) + let end = selectionText.coordinate(withNormalizedOffset: CGVector(dx: 0.6, dy: 0.5)) + start.press(forDuration: 0.8, thenDragTo: end) + + // 等待选区状态 + Thread.sleep(forTimeInterval: 0.5) + + // 点击批注菜单项 + let annotateItem = app.menuItems["批注"] + guard annotateItem.waitForExistence(timeout: 3) else { + throw XCTSkip("批注菜单项不存在") + } + annotateItem.tap() + + // 验证高亮数量变为 1 + app.waitForDemoReaderState(timeout: 5, description: "highlights=1") { $0.highlights == 1 } + } + + /// 高亮跨重启存活 + func testHighlightSurvivesRestart() throws { + app.launchAndOpenSampleBook() + app.waitForReader() + + // 创建高亮 + let selectionText = app.otherElements[IDs.readerSelectionText].firstMatch + guard selectionText.waitForExistence(timeout: 5) else { + throw XCTSkip("文本选区元素不存在") + } + + let start = selectionText.coordinate(withNormalizedOffset: CGVector(dx: 0.3, dy: 0.5)) + let end = selectionText.coordinate(withNormalizedOffset: CGVector(dx: 0.6, dy: 0.5)) + start.press(forDuration: 0.8, thenDragTo: end) + + Thread.sleep(forTimeInterval: 0.5) + + let annotateItem = app.menuItems["批注"] + guard annotateItem.waitForExistence(timeout: 3) else { + throw XCTSkip("批注菜单项不存在") + } + annotateItem.tap() + + // 等待高亮创建完成 + app.waitForDemoReaderState(timeout: 5, description: "highlights=1") { $0.highlights == 1 } + + // 关闭阅读器 + app.buttons[IDs.readerBack].tap() + + // 重新打开(不重置状态) + app.launchAndOpenSampleBook(resetsReaderState: false) + app.waitForReader() + + // 验证高亮仍然存在 + app.waitForDemoReaderState(timeout: 8, description: "highlights=1 after restart") { $0.highlights == 1 } + } + + /// 重排后高亮跳转页码应正确(多页覆盖) + /// 回归:chapterOffset(for:) 曾优先使用 row/column 查表而非存储的 chapterOffset, + /// 导致重新排版后跳转到错误的页面 + func testHighlightNavigationAfterRepagination() throws { + // 测试多页:在不同页面创建高亮,改变字体后跳转,验证页码正确 + let testPages = [2, 3, 5] + + for page in testPages { + // 每轮重新启动以确保干净状态 + app.launchAndOpenSampleBook(pageNumber: page) + app.waitForReader() + + let stateBefore = app.waitForDemoReaderState(timeout: 5, description: "at page \(page)") { $0.page == page } + XCTAssertEqual(stateBefore.page, page, "应成功打开第 \(page) 页") + + // 创建高亮 + let selectionText = app.otherElements[IDs.readerSelectionText].firstMatch + guard selectionText.waitForExistence(timeout: 5) else { + throw XCTSkip("文本选区元素不存在") + } + let start = selectionText.coordinate(withNormalizedOffset: CGVector(dx: 0.2, dy: 0.5)) + let end = selectionText.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)) + start.press(forDuration: 0.8, thenDragTo: end) + Thread.sleep(forTimeInterval: 0.5) + + let highlightItem = app.menuItems["高亮"] + guard highlightItem.waitForExistence(timeout: 3) else { + throw XCTSkip("高亮菜单项不存在") + } + highlightItem.tap() + app.waitForDemoReaderState(timeout: 5, description: "highlights=1 on page \(page)") { $0.highlights == 1 } + + // 改变字体大小触发重新排版 + app.showReaderChromeIfNeeded() + let settingsButton = app.buttons[IDs.readerSettings] + XCTAssertTrue(settingsButton.waitForExistence(timeout: 3), "设置按钮应存在") + settingsButton.tap() + + let fontIncrease = app.buttons[IDs.settingsFontIncrease] + XCTAssertTrue(fontIncrease.waitForExistence(timeout: 3), "字号增大按钮应存在") + fontIncrease.tap() + fontIncrease.tap() + Thread.sleep(forTimeInterval: 0.5) + + let doneButton = app.buttons[IDs.settingsDone] + if doneButton.waitForExistence(timeout: 3) { + doneButton.tap() + } + Thread.sleep(forTimeInterval: 1) + + // 翻到其他页 + let content = app.otherElements[IDs.readerContent].firstMatch + if content.waitForExistence(timeout: 3) { + for _ in 0..<3 { + content.swipeLeft() + Thread.sleep(forTimeInterval: 0.3) + } + Thread.sleep(forTimeInterval: 0.5) + } + + // 通过高亮面板跳转回高亮位置 + app.showReaderChromeIfNeeded() + let highlightsButton = app.buttons[IDs.readerHighlights] + XCTAssertTrue(highlightsButton.waitForExistence(timeout: 3), "高亮列表按钮应存在") + highlightsButton.tap() + + let highlightsTable = app.tables[IDs.readerHighlightsTable] + XCTAssertTrue(highlightsTable.waitForExistence(timeout: 5), "高亮列表应出现") + + let firstCell = highlightsTable.cells.firstMatch + XCTAssertTrue(firstCell.waitForExistence(timeout: 3), "高亮列表应有至少一个条目") + firstCell.tap() + + // 等待跳转完成 + Thread.sleep(forTimeInterval: 1) + + // 验证跳转后的页码应为创建高亮时的页码 + let stateAfter = app.waitForDemoReaderState(timeout: 5, description: "back at page \(page) after repagination") { + $0.page == page + } + XCTAssertEqual(stateAfter.page, page, "重排后高亮跳转应回到第 \(page) 页,实际:\(stateAfter.page ?? -1)") + } + } +} diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SelectionMenuTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SelectionMenuTests.swift index 9b64817..b57c293 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SelectionMenuTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SelectionMenuTests.swift @@ -71,6 +71,6 @@ final class SelectionMenuTests: XCTestCase { let copyMenuItem = app.menuItems["拷贝"] if copyMenuItem.waitForExistence(timeout: 3) { copyMenuItem.tap() } - app.waitForReaderState(containing: "reader=opened", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "reader=opened") { $0.isOpened } } } \ No newline at end of file diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SettingsEffectTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SettingsEffectTests.swift index 2322750..e63b9cf 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SettingsEffectTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SettingsEffectTests.swift @@ -10,7 +10,7 @@ final class SettingsEffectTests: XCTestCase { func testFontSizeChangeUpdatesContent() throws { app.launchAndOpenSampleBook() app.waitForReader(timeout: 12) - app.waitForReaderState(containing: "page=", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "page exists") { $0.page != nil } app.showReaderChromeIfNeeded() XCTAssertTrue(app.buttons[IDs.readerSettings].waitForExistence(timeout: 3), "设置按钮不存在") @@ -23,7 +23,7 @@ final class SettingsEffectTests: XCTestCase { XCTAssertTrue(app.buttons[IDs.settingsDone].waitForExistence(timeout: 3), "完成按钮不存在") app.buttons[IDs.settingsDone].tap() - app.waitForReaderState(containing: "reader=opened", timeout: 8) + app.waitForDemoReaderState(timeout: 8, description: "reader=opened") { $0.isOpened } } func testThemeSwitchChangesBackground() throws { @@ -59,7 +59,7 @@ final class SettingsEffectTests: XCTestCase { } app.buttons[IDs.settingsDone].tap() - app.waitForReaderState(containing: "reader=opened", timeout: 8) + app.waitForDemoReaderState(timeout: 8, description: "reader=opened") { $0.isOpened } } func testLineHeightChangeUpdatesContent() throws { @@ -76,13 +76,13 @@ final class SettingsEffectTests: XCTestCase { } app.buttons[IDs.settingsDone].tap() - app.waitForReaderState(containing: "reader=opened", timeout: 8) + app.waitForDemoReaderState(timeout: 8, description: "reader=opened") { $0.isOpened } } func testDisplayTypeChangeRePaginates() throws { app.launchAndOpenSampleBook(displayType: "horizontalscroll") app.waitForReader(timeout: 12) - app.waitForReaderState(containing: "display=horizontalScroll", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "display=horizontalScroll") { $0.display == "horizontalScroll" } app.showReaderChromeIfNeeded() app.buttons[IDs.readerSettings].tap() @@ -94,7 +94,7 @@ final class SettingsEffectTests: XCTestCase { } app.buttons[IDs.settingsDone].tap() - app.waitForReaderState(containing: "display=", timeout: 8) + app.waitForDemoReaderState(timeout: 8, description: "display changed") { $0.display != nil } } func testDefaultBodyPaginationDoesNotEnableWidowOrphanCompaction() throws { @@ -141,4 +141,60 @@ final class SettingsEffectTests: XCTestCase { app.buttons[IDs.settingsDone].tap() } + + func testThemeSelectionPersistsAfterReopen() throws { + app.launchAndOpenSampleBook(resetsReaderState: true) + app.waitForReader(timeout: 12) + + app.showReaderChromeIfNeeded() + app.buttons[IDs.readerSettings].tap() + + let darkThemeButton = app.buttons[IDs.settingsTheme(5)] + XCTAssertTrue(darkThemeButton.waitForExistence(timeout: 5)) + darkThemeButton.tap() + XCTAssertEqual(darkThemeButton.value as? String, "selected", "切换主题后当前主题按钮应为选中态") + app.buttons[IDs.settingsDone].tap() + + app.showReaderChromeIfNeeded() + app.buttons[IDs.readerBack].tap() + + app.launchAndOpenSampleBook(resetsReaderState: false) + app.waitForReader(timeout: 12) + app.showReaderChromeIfNeeded() + app.buttons[IDs.readerSettings].tap() + + let reopenedDarkThemeButton = app.buttons[IDs.settingsTheme(5)] + XCTAssertTrue(reopenedDarkThemeButton.waitForExistence(timeout: 5)) + XCTAssertEqual(reopenedDarkThemeButton.value as? String, "selected", "主题应在重启后保持选中") + app.buttons[IDs.settingsDone].tap() + } + + func testFontChoiceChangePersistsAfterReopen() throws { + app.launchAndOpenSampleBook(resetsReaderState: true) + app.waitForReader(timeout: 12) + + app.showReaderChromeIfNeeded() + app.buttons[IDs.readerSettings].tap() + + let fontChoiceControl = app.segmentedControls[IDs.settingsFontChoice] + XCTAssertTrue(fontChoiceControl.waitForExistence(timeout: 5)) + if fontChoiceControl.buttons.count > 1 { + fontChoiceControl.buttons.element(boundBy: 1).tap() + } + XCTAssertEqual(fontChoiceControl.value as? String, "宋体", "切换字体后当前字体值应更新") + app.buttons[IDs.settingsDone].tap() + + app.showReaderChromeIfNeeded() + app.buttons[IDs.readerBack].tap() + + app.launchAndOpenSampleBook(resetsReaderState: false) + app.waitForReader(timeout: 12) + app.showReaderChromeIfNeeded() + app.buttons[IDs.readerSettings].tap() + + let reopenedFontChoiceControl = app.segmentedControls[IDs.settingsFontChoice] + XCTAssertTrue(reopenedFontChoiceControl.waitForExistence(timeout: 5)) + XCTAssertEqual(reopenedFontChoiceControl.value as? String, "宋体", "字体选择应在重启后保持") + app.buttons[IDs.settingsDone].tap() + } } diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SettingsExtendedTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SettingsExtendedTests.swift index 1fc4bd8..a1909ea 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SettingsExtendedTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/SettingsExtendedTests.swift @@ -55,7 +55,7 @@ final class SettingsExtendedTests: XCTestCase { displayType.buttons["横滑"].tap() app.buttons[IDs.settingsDone].tap() - app.waitForReaderState(containing: "display=horizontalScroll", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "display=horizontalScroll") { $0.display == "horizontalScroll" } } func testAllThemeButtonsExist() { diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/TOCInteractionTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/TOCInteractionTests.swift index 420ce04..1dc86b8 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/TOCInteractionTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/TOCInteractionTests.swift @@ -47,7 +47,7 @@ final class TOCInteractionTests: XCTestCase { XCTAssertNotEqual(updatedPage, initialPage, "目录跳转后页码应变化") } else { tocTable.cells.element(boundBy: 0).tap() - app.waitForReaderState(containing: "reader=opened", timeout: 8) + app.waitForDemoReaderState(timeout: 8, description: "reader=opened") { $0.isOpened } } } diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/TableOfContentsTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/TableOfContentsTests.swift index 4d4c758..dcd3424 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/TableOfContentsTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/TableOfContentsTests.swift @@ -10,7 +10,7 @@ final class TableOfContentsTests: XCTestCase { func testTocButtonExists() { app.launchAndOpenSampleBook(bookTitleQuery: "宝山辽墓材料与释读") app.waitForReader() - app.showReaderChromeIfNeeded() + app.showReaderChromeIfNeeded(requiredButtonIDs: [IDs.readerToc]) let tocButton = app.buttons[IDs.readerToc] XCTAssertTrue(tocButton.waitForExistence(timeout: 5), "目录按钮未出现") @@ -19,7 +19,7 @@ final class TableOfContentsTests: XCTestCase { func testTocButtonTappable() { app.launchAndOpenSampleBook(bookTitleQuery: "宝山辽墓材料与释读") app.waitForReader() - app.showReaderChromeIfNeeded() + app.showReaderChromeIfNeeded(requiredButtonIDs: [IDs.readerToc]) let tocButton = app.buttons[IDs.readerToc] XCTAssertTrue(tocButton.waitForExistence(timeout: 5)) diff --git a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ToolbarStateTests.swift b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ToolbarStateTests.swift index 127c79e..a511da5 100644 --- a/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ToolbarStateTests.swift +++ b/ReadViewDemo/ReadViewDemoUITests/ReaderUITests/ToolbarStateTests.swift @@ -35,7 +35,7 @@ final class ToolbarStateTests: XCTestCase { app.showReaderChromeIfNeeded() XCTAssertTrue(app.buttons[IDs.readerBack].waitForExistence(timeout: 3)) - app.waitForReaderState(containing: "reader=opened", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "reader=opened") { $0.isOpened } } func testAddHighlightButtonDisabledWithoutSelection() throws { @@ -66,6 +66,6 @@ final class ToolbarStateTests: XCTestCase { let paging = app.collectionViews[IDs.readerPaging] if paging.waitForExistence(timeout: 5) { paging.swipeLeft() } - app.waitForReaderState(containing: "reader=opened", timeout: 5) + app.waitForDemoReaderState(timeout: 5, description: "reader=opened") { $0.isOpened } } } diff --git a/Sources/RDReaderView/EPUBCore/RDEPUBJavaScriptBridge.swift b/Sources/RDReaderView/EPUBCore/RDEPUBJavaScriptBridge.swift index e8dc86a..6fbd06d 100644 --- a/Sources/RDReaderView/EPUBCore/RDEPUBJavaScriptBridge.swift +++ b/Sources/RDReaderView/EPUBCore/RDEPUBJavaScriptBridge.swift @@ -72,7 +72,11 @@ enum RDEPUBJavaScriptBridge { window.WeReadApi.clearHighlights(); window.WeReadApi.setHighlights(\(jsonString(from: highlightsPayload(request.highlights), fallback: "[]"))); if (\(jsonString(from: targetLocationPayload(request.targetLocation), fallback: "null")) !== null) { - window.WeReadApi.scrollToLocation(\(jsonString(from: targetLocationPayload(request.targetLocation), fallback: "null")), \(request.pageIndex)); + window.WeReadApi.scrollToLocation( + \(jsonString(from: targetLocationPayload(request.targetLocation), fallback: "null")), + \(request.pageIndex), + \(javaScriptStringLiteral(request.targetHighlightRangeInfo)) + ); } else { window.WeReadApi.scrollToPage(\(request.pageIndex)); } diff --git a/Sources/RDReaderView/EPUBCore/RDEPUBModels.swift b/Sources/RDReaderView/EPUBCore/RDEPUBModels.swift index 0eef6b9..dcfd66e 100644 --- a/Sources/RDReaderView/EPUBCore/RDEPUBModels.swift +++ b/Sources/RDReaderView/EPUBCore/RDEPUBModels.swift @@ -212,6 +212,8 @@ public enum RDEPUBParserError: LocalizedError { case missingManifestItem(idref: String) /// 构建完成的 spine 为空(无可阅读的内容) case emptySpine + /// 归档条目路径包含路径穿越或非法字符 + case invalidArchiveEntryPath(String) public var errorDescription: String? { switch self { @@ -229,6 +231,8 @@ public enum RDEPUBParserError: LocalizedError { return "spine itemref 找不到对应 manifest 项: \(idref)" case .emptySpine: return "OPF spine 为空" + case .invalidArchiveEntryPath(let path): + return "归档条目路径非法: \(path)" } } } \ No newline at end of file diff --git a/Sources/RDReaderView/EPUBCore/RDEPUBPaginator.swift b/Sources/RDReaderView/EPUBCore/RDEPUBPaginator.swift index 195f4cd..684205a 100644 --- a/Sources/RDReaderView/EPUBCore/RDEPUBPaginator.swift +++ b/Sources/RDReaderView/EPUBCore/RDEPUBPaginator.swift @@ -53,9 +53,7 @@ public final class RDEPUBPaginator: NSObject { webView.scrollView.showsVerticalScrollIndicator = false webView.isUserInteractionEnabled = false if #available(iOS 16.4, *) { - webView.isInspectable = true - } else { - // Fallback on earlier versions + webView.isInspectable = RDEPUBWebViewDebug.isInspectableEnabled } RDEPUBWebViewDebug.log("PaginatorWebView", message: "configured webView=\(RDEPUBWebViewDebug.webViewID(webView))") return webView @@ -297,6 +295,9 @@ public final class RDEPUBPaginator: NSObject { return mediaType.contains("html") || mediaType.contains("xhtml") || mediaType.contains("xml") } + /// 各轮测量记录值(用于稳定性日志) + private var measurementPassValues: [Int] = [] + /// 调度多轮测量:0ms/80ms/180ms 三轮延迟,取最大值以应对布局抖动 private func scheduleMeasurementPass() { let sessionID = activeSessionID @@ -306,8 +307,18 @@ public final class RDEPUBPaginator: NSObject { let currentSpineIndex = currentSpineIndexForMeasurement() else { return } - pageCounts[currentSpineIndex] = max(1, pendingMeasurementValue) + let finalValue = max(1, pendingMeasurementValue) + // 测量稳定性日志:三轮差异过大时告警 + if measurementPassValues.count >= 2 { + let minVal = measurementPassValues.min() ?? 1 + let maxVal = measurementPassValues.max() ?? 1 + if minVal > 0 && Double(maxVal - minVal) / Double(minVal) > 0.2 { + RDEPUBWebViewDebug.log(debugScope, message: "measurement instability: passes=\(measurementPassValues) spine=\(currentSpineIndex)") + } + } + pageCounts[currentSpineIndex] = finalValue currentMeasurementOffset += 1 + measurementPassValues = [] measureNextSpineItem() return } @@ -333,8 +344,10 @@ public final class RDEPUBPaginator: NSObject { } if let number = value as? NSNumber { self.pendingMeasurementValue = max(self.pendingMeasurementValue, number.intValue) + self.measurementPassValues.append(number.intValue) } else if let intValue = value as? Int { self.pendingMeasurementValue = max(self.pendingMeasurementValue, intValue) + self.measurementPassValues.append(intValue) } RDEPUBWebViewDebug.log(self.debugScope, message: "measurement value=\(self.pendingMeasurementValue) session=\(sessionID)") self.scheduleMeasurementPass() @@ -344,6 +357,20 @@ public final class RDEPUBPaginator: NSObject { } extension RDEPUBPaginator: WKNavigationDelegate { + public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { + guard let url = navigationAction.request.url else { + decisionHandler(.cancel) + return + } + let scheme = url.scheme?.lowercased() ?? "" + if scheme == "file" || scheme == RDEPUBResourceURLSchemeHandler.scheme { + decisionHandler(.allow) + } else { + RDEPUBWebViewDebug.log(debugScope, message: "blocked navigation to non-local scheme: \(scheme)") + decisionHandler(.cancel) + } + } + public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { RDEPUBWebViewDebug.logNavigationEvent(debugScope, webView: webView, event: "didStart", url: webView.url) } diff --git a/Sources/RDReaderView/EPUBCore/RDEPUBParser+Archive.swift b/Sources/RDReaderView/EPUBCore/RDEPUBParser+Archive.swift index 5507994..61381d9 100644 --- a/Sources/RDReaderView/EPUBCore/RDEPUBParser+Archive.swift +++ b/Sources/RDReaderView/EPUBCore/RDEPUBParser+Archive.swift @@ -49,7 +49,9 @@ extension RDEPUBParser { try fileManager.createDirectory(at: extractionURL, withIntermediateDirectories: true) for entry in archive { - let destinationURL = extractionURL.appendingPathComponent(entry.path) + guard let destinationURL = validatedExtractionDestination(for: entry.path, extractionRoot: extractionURL) else { + throw RDEPUBParserError.invalidArchiveEntryPath(entry.path) + } switch entry.type { case .directory: try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true) @@ -64,6 +66,31 @@ extension RDEPUBParser { return extractionURL } + /// 校验归档条目路径,防止路径穿越攻击 + /// - Parameters: + /// - entryPath: 归档中的原始条目路径 + /// - extractionRoot: 解压根目录 + /// - Returns: 校验通过的目标 URL,非法路径返回 nil + private func validatedExtractionDestination(for entryPath: String, extractionRoot: URL) -> URL? { + // 拒绝绝对路径 + if entryPath.hasPrefix("/") { + return nil + } + // 拒绝包含 .. 的路径段 + let components = entryPath.split(separator: "/", omittingEmptySubsequences: true) + if components.contains(where: { $0 == ".." }) { + return nil + } + let destinationURL = extractionRoot.appendingPathComponent(entryPath) + let standardizedDest = destinationURL.standardizedFileURL.path + let standardizedRoot = extractionRoot.standardizedFileURL.path + // 确保最终路径位于解压根目录下 + guard standardizedDest.hasPrefix(standardizedRoot) else { + return nil + } + return destinationURL + } + /// 计算 EPUB 的临时解压目录路径 /// 路径格式:~/Library/Caches/ssreaderview-epub/{slug}-{fileSize}-{modifiedTimestamp}/ func temporaryExtractionDirectory(for epubURL: URL) -> URL { diff --git a/Sources/RDReaderView/EPUBCore/RDEPUBPreferences.swift b/Sources/RDReaderView/EPUBCore/RDEPUBPreferences.swift index b9b5b72..ec7d93e 100644 --- a/Sources/RDReaderView/EPUBCore/RDEPUBPreferences.swift +++ b/Sources/RDReaderView/EPUBCore/RDEPUBPreferences.swift @@ -76,6 +76,7 @@ public struct RDEPUBPreferences: Equatable { publication: RDEPUBPublication, viewportSize: CGSize, targetLocation: RDEPUBLocation? = nil, + targetHighlightRangeInfo: String? = nil, highlights: [RDEPUBHighlight] = [], searchPresentation: RDEPUBSearchPresentation? = nil ) -> RDEPUBRenderRequest? { @@ -104,6 +105,7 @@ public struct RDEPUBPreferences: Equatable { totalPagesInChapter: page.totalPagesInChapter, presentation: presentationStyle(viewportSize: viewportSize), targetLocation: targetLocation, + targetHighlightRangeInfo: targetHighlightRangeInfo, highlights: highlights, searchPresentation: searchPresentation ) diff --git a/Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift b/Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift index 442f39f..4a00499 100644 --- a/Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift +++ b/Sources/RDReaderView/EPUBCore/RDEPUBReadingSession.swift @@ -31,6 +31,8 @@ public final class RDEPUBReadingSession { public private(set) var pendingNavigationLocation: RDEPUBLocation? /// 挂起的导航目标页码 public private(set) var pendingNavigationPageNum: Int? + /// 挂起的高亮 Range 信息(仅 Web 渲染路径使用) + public private(set) var pendingNavigationHighlightRangeInfo: String? /// 当前视口信息 public private(set) var currentViewport: RDEPUBViewport? /// 当前阅读上下文(位置 + 视口 + 页码 + 章节) @@ -105,6 +107,7 @@ public final class RDEPUBReadingSession { public func clearPendingNavigation() { pendingNavigationLocation = nil pendingNavigationPageNum = nil + pendingNavigationHighlightRangeInfo = nil } /// 检查指定页码和 spine 索引是否有挂起的导航位置 @@ -123,6 +126,26 @@ public final class RDEPUBReadingSession { return pendingNavigationLocation } + /// 查询指定页是否存在待消费的高亮 Range 信息。 + public func pendingHighlightRangeInfo(forPageNumber pageNumber: Int, spineIndex: Int?) -> String? { + guard pendingNavigationPageNum == pageNumber, + let pendingNavigationHighlightRangeInfo, + !pendingNavigationHighlightRangeInfo.isEmpty else { + return nil + } + guard let pendingNavigationLocation else { + return pendingNavigationHighlightRangeInfo + } + guard let spineIndex else { + return pendingNavigationHighlightRangeInfo + } + let pageHref = resourceResolver.href(forSpineIndex: spineIndex) + guard resourceResolver.normalizedHref(pageHref ?? "") == resourceResolver.normalizedHref(pendingNavigationLocation.href) else { + return nil + } + return pendingNavigationHighlightRangeInfo + } + /// 判断指定页面是否包含给定的 spine 索引(固定版式需检查 spread 内所有资源) public func pageContains(spineIndex: Int, in page: EPUBPage) -> Bool { if let fixedSpread = page.fixedSpread { @@ -225,7 +248,8 @@ public final class RDEPUBReadingSession { public func queueNavigation( to location: RDEPUBLocation, relativeToSpineIndex spineIndex: Int? = nil, - bookIdentifier: String? + bookIdentifier: String?, + targetHighlightRangeInfo: String? = nil ) -> Int? { guard let normalizedLocation = resourceResolver.normalizedLocation( location, @@ -235,8 +259,14 @@ public final class RDEPUBReadingSession { return nil } - pendingNavigationLocation = normalizedLocation.fragment == nil ? nil : normalizedLocation - pendingNavigationPageNum = normalizedLocation.fragment == nil ? nil : pageIndex + 1 + let hasTargetHighlightRangeInfo = targetHighlightRangeInfo?.isEmpty == false + let shouldKeepPendingNavigation = + normalizedLocation.fragment != nil || + normalizedLocation.rangeAnchor != nil || + hasTargetHighlightRangeInfo + pendingNavigationLocation = shouldKeepPendingNavigation ? normalizedLocation : nil + pendingNavigationPageNum = shouldKeepPendingNavigation ? pageIndex + 1 : nil + pendingNavigationHighlightRangeInfo = hasTargetHighlightRangeInfo ? targetHighlightRangeInfo : nil transition(to: .jumping) return pageIndex + 1 } @@ -363,4 +393,4 @@ public final class RDEPUBReadingSession { return (pages, chapters) } -} \ No newline at end of file +} diff --git a/Sources/RDReaderView/EPUBCore/RDEPUBRenderRequest.swift b/Sources/RDReaderView/EPUBCore/RDEPUBRenderRequest.swift index ac29f70..c88bbfd 100644 --- a/Sources/RDReaderView/EPUBCore/RDEPUBRenderRequest.swift +++ b/Sources/RDReaderView/EPUBCore/RDEPUBRenderRequest.swift @@ -85,6 +85,8 @@ public struct RDEPUBReflowableRenderRequest: Equatable { public var presentation: RDEPUBPresentationStyle /// 目标跳转位置(用于恢复阅读位置或锚点跳转) public var targetLocation: RDEPUBLocation? + /// 目标高亮的 DOM Range 信息,用于 Web 渲染路径下的精确跳转 + public var targetHighlightRangeInfo: String? /// 高亮列表 public var highlights: [RDEPUBHighlight] /// 搜索结果展示信息 @@ -97,6 +99,7 @@ public struct RDEPUBReflowableRenderRequest: Equatable { totalPagesInChapter: Int, presentation: RDEPUBPresentationStyle, targetLocation: RDEPUBLocation? = nil, + targetHighlightRangeInfo: String? = nil, highlights: [RDEPUBHighlight] = [], searchPresentation: RDEPUBSearchPresentation? = nil ) { @@ -106,6 +109,7 @@ public struct RDEPUBReflowableRenderRequest: Equatable { self.totalPagesInChapter = totalPagesInChapter self.presentation = presentation self.targetLocation = targetLocation + self.targetHighlightRangeInfo = targetHighlightRangeInfo self.highlights = highlights self.searchPresentation = searchPresentation } diff --git a/Sources/RDReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift b/Sources/RDReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift index 0d28843..acd551c 100644 --- a/Sources/RDReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift +++ b/Sources/RDReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift @@ -8,6 +8,12 @@ import WebKit /// 自定义 URL 协议处理器,将 ss-reader://book/ 请求映射到本地 EPUB 资源文件 public final class RDEPUBResourceURLSchemeHandler: NSObject, WKURLSchemeHandler { + struct DebugMetrics { + let streamedResponses: Int + let inMemoryResponses: Int + let failures: Int + } + /// 自定义协议名 public static let scheme = "ss-reader" /// 自定义主机名 @@ -21,12 +27,34 @@ public final class RDEPUBResourceURLSchemeHandler: NSObject, WKURLSchemeHandler private let syncQueue = DispatchQueue(label: "com.ssreaderview.epub.scheme-handler") /// 活跃的 URL Scheme 任务集合(用于任务取消检测) private var activeTasks: [ObjectIdentifier: Bool] = [:] + private static let debugMetricsQueue = DispatchQueue(label: "com.ssreaderview.epub.scheme-handler.metrics") + private static var streamedResponseCount = 0 + private static var inMemoryResponseCount = 0 + private static var failureCount = 0 public init(parser: RDEPUBParser) { self.parser = parser super.init() } + static func resetDebugMetrics() { + debugMetricsQueue.sync { + streamedResponseCount = 0 + inMemoryResponseCount = 0 + failureCount = 0 + } + } + + static func debugMetricsSnapshot() -> DebugMetrics { + debugMetricsQueue.sync { + DebugMetrics( + streamedResponses: streamedResponseCount, + inMemoryResponses: inMemoryResponseCount, + failures: failureCount + ) + } + } + /// 处理 URL Scheme 请求:解析文件路径、读取数据、返回响应 /// 对缺失的可选资源(字体/图片/CSS/JS)返回空响应,其他返回 404 错误 public func webView(_ webView: WKWebView, start urlSchemeTask: any WKURLSchemeTask) { @@ -55,6 +83,7 @@ public final class RDEPUBResourceURLSchemeHandler: NSObject, WKURLSchemeHandler urlSchemeTask.didFinish() } else { RDEPUBWebViewDebug.logSchemeTask("ResourceScheme", requestURL: requestURL, event: "missing-file") + Self.recordFailure() urlSchemeTask.didFailWithError(NSError(domain: NSURLErrorDomain, code: NSURLErrorFileDoesNotExist)) } clearTask(taskID) @@ -63,27 +92,12 @@ public final class RDEPUBResourceURLSchemeHandler: NSObject, WKURLSchemeHandler RDEPUBWebViewDebug.logSchemeTask("ResourceScheme", requestURL: requestURL, fileURL: fileURL, event: "resolved") - do { - let data = try Data(contentsOf: fileURL) - guard isTaskActive(taskID) else { return } - - let response = URLResponse( - url: requestURL, - mimeType: Self.mimeType(for: fileURL.pathExtension), - expectedContentLength: data.count, - textEncodingName: Self.textEncodingName(for: fileURL.pathExtension) - ) - urlSchemeTask.didReceive(response) - urlSchemeTask.didReceive(data) - urlSchemeTask.didFinish() - RDEPUBWebViewDebug.logSchemeTask("ResourceScheme", requestURL: requestURL, fileURL: fileURL, event: "finished") - } catch { - guard isTaskActive(taskID) else { return } - RDEPUBWebViewDebug.logSchemeTask("ResourceScheme", requestURL: requestURL, fileURL: fileURL, event: "failed", error: error) - urlSchemeTask.didFailWithError(error) + let fileSize = resourceSize(for: fileURL) + if let fileSize, fileSize > 524_288 { + respondWithStreaming(fileURL: fileURL, requestURL: requestURL, taskID: taskID, urlSchemeTask: urlSchemeTask, fileSize: fileSize) + } else { + respondWithInMemoryData(fileURL: fileURL, requestURL: requestURL, taskID: taskID, urlSchemeTask: urlSchemeTask) } - - clearTask(taskID) } /// 取消 URL Scheme 任务 @@ -106,6 +120,89 @@ public final class RDEPUBResourceURLSchemeHandler: NSObject, WKURLSchemeHandler } } + private func resourceSize(for url: URL) -> UInt64? { + guard let attrs = try? fileManager.attributesOfItem(atPath: url.path), + let size = attrs[.size] as? UInt64 else { + return nil + } + return size + } + + private func respondWithInMemoryData(fileURL: URL, requestURL: URL, taskID: ObjectIdentifier, urlSchemeTask: any WKURLSchemeTask) { + do { + let data = try Data(contentsOf: fileURL) + guard isTaskActive(taskID) else { return } + let response = URLResponse( + url: requestURL, + mimeType: Self.mimeType(for: fileURL.pathExtension), + expectedContentLength: data.count, + textEncodingName: Self.textEncodingName(for: fileURL.pathExtension) + ) + urlSchemeTask.didReceive(response) + urlSchemeTask.didReceive(data) + urlSchemeTask.didFinish() + Self.recordInMemoryResponse() + RDEPUBWebViewDebug.logSchemeTask("ResourceScheme", requestURL: requestURL, fileURL: fileURL, event: "finished") + } catch { + guard isTaskActive(taskID) else { return } + Self.recordFailure() + RDEPUBWebViewDebug.logSchemeTask("ResourceScheme", requestURL: requestURL, fileURL: fileURL, event: "failed", error: error) + urlSchemeTask.didFailWithError(error) + } + clearTask(taskID) + } + + private func respondWithStreaming(fileURL: URL, requestURL: URL, taskID: ObjectIdentifier, urlSchemeTask: any WKURLSchemeTask, fileSize: UInt64) { + let response = URLResponse( + url: requestURL, + mimeType: Self.mimeType(for: fileURL.pathExtension), + expectedContentLength: Int(fileSize), + textEncodingName: Self.textEncodingName(for: fileURL.pathExtension) + ) + urlSchemeTask.didReceive(response) + + guard let fileHandle = try? FileHandle(forReadingFrom: fileURL) else { + guard isTaskActive(taskID) else { return } + Self.recordFailure() + urlSchemeTask.didFailWithError(NSError(domain: NSURLErrorDomain, code: NSURLErrorFileDoesNotExist)) + clearTask(taskID) + return + } + + let chunkSize = 65_536 + defer { + fileHandle.closeFile() + clearTask(taskID) + } + while true { + guard isTaskActive(taskID) else { return } + let data = fileHandle.readData(ofLength: chunkSize) + if data.isEmpty { break } + urlSchemeTask.didReceive(data) + } + urlSchemeTask.didFinish() + Self.recordStreamedResponse() + RDEPUBWebViewDebug.logSchemeTask("ResourceScheme", requestURL: requestURL, fileURL: fileURL, event: "finished-streaming") + } + + private static func recordStreamedResponse() { + debugMetricsQueue.sync { + streamedResponseCount += 1 + } + } + + private static func recordInMemoryResponse() { + debugMetricsQueue.sync { + inMemoryResponseCount += 1 + } + } + + private static func recordFailure() { + debugMetricsQueue.sync { + failureCount += 1 + } + } + /// 根据文件扩展名推断 MIME 类型 private static func mimeType(for pathExtension: String) -> String { switch pathExtension.lowercased() { @@ -165,4 +262,4 @@ public final class RDEPUBResourceURLSchemeHandler: NSObject, WKURLSchemeHandler return false } } -} \ No newline at end of file +} diff --git a/Sources/RDReaderView/EPUBCore/RDEPUBWebView+Configuration.swift b/Sources/RDReaderView/EPUBCore/RDEPUBWebView+Configuration.swift index b6a9507..236a2c5 100644 --- a/Sources/RDReaderView/EPUBCore/RDEPUBWebView+Configuration.swift +++ b/Sources/RDReaderView/EPUBCore/RDEPUBWebView+Configuration.swift @@ -60,7 +60,7 @@ extension RDEPUBWebView { webView.backgroundColor = .clear webView.clipsToBounds = true if #available(iOS 16.4, *) { - webView.isInspectable = true + webView.isInspectable = RDEPUBWebViewDebug.isInspectableEnabled } addSubview(webView) diff --git a/Sources/RDReaderView/EPUBCore/RDEPUBWebView+Reflowable.swift b/Sources/RDReaderView/EPUBCore/RDEPUBWebView+Reflowable.swift index cf625b0..264d414 100644 --- a/Sources/RDReaderView/EPUBCore/RDEPUBWebView+Reflowable.swift +++ b/Sources/RDReaderView/EPUBCore/RDEPUBWebView+Reflowable.swift @@ -97,7 +97,8 @@ extension RDEPUBWebView { request.targetLocation?.href ?? "", String(request.targetLocation?.progression ?? 0), String(request.targetLocation?.lastProgression ?? 0), - request.targetLocation?.fragment ?? "" + request.targetLocation?.fragment ?? "", + request.targetHighlightRangeInfo ?? "" ].joined(separator: "|") let highlightSignature = request.highlights .map { [$0.id, $0.rangeInfo ?? "", $0.color, $0.style.rawValue].joined(separator: "|") } diff --git a/Sources/RDReaderView/EPUBCore/RDEPUBWebView+Search.swift b/Sources/RDReaderView/EPUBCore/RDEPUBWebView+Search.swift index 2825470..a75b14e 100644 --- a/Sources/RDReaderView/EPUBCore/RDEPUBWebView+Search.swift +++ b/Sources/RDReaderView/EPUBCore/RDEPUBWebView+Search.swift @@ -6,6 +6,14 @@ import UIKit import WebKit extension RDEPUBWebView { + private var normalSearchOverlayColor: UIColor { + UIColor(red: 0.21, green: 0.48, blue: 0.95, alpha: 0.16) + } + + private var activeSearchOverlayColor: UIColor { + UIColor(red: 0.14, green: 0.42, blue: 0.95, alpha: 0.34) + } + /// 如果当前渲染请求包含搜索信息,注入搜索高亮脚本到 WebView func applySearchDecorationsIfNeeded(completion: (() -> Void)? = nil) { guard let webView else { @@ -70,7 +78,7 @@ extension RDEPUBWebView { ) let searchDecorations = decorationList( from: payload["search"] as? [[String: Any]], - defaultColor: UIColor(red: 248 / 255, green: 225 / 255, blue: 108 / 255, alpha: 0.55) + defaultColor: normalSearchOverlayColor ) return highlightDecorations + searchDecorations } @@ -105,9 +113,9 @@ extension RDEPUBWebView { let hex = item["color"] as? String ?? "#F8E16C" color = overlayColor(hex: hex, alpha: 1) ?? defaultColor case .activeSearch: - color = UIColor(red: 255 / 255, green: 159 / 255, blue: 67 / 255, alpha: 0.75) + color = activeSearchOverlayColor case .search: - color = UIColor(red: 248 / 255, green: 225 / 255, blue: 108 / 255, alpha: 0.55) + color = normalSearchOverlayColor case .highlight: let hex = item["color"] as? String ?? "#F8E16C" color = overlayColor(hex: hex, alpha: 0.45) ?? defaultColor diff --git a/Sources/RDReaderView/EPUBCore/RDEPUBWebViewDebug.swift b/Sources/RDReaderView/EPUBCore/RDEPUBWebViewDebug.swift index e7cbd1a..23e560f 100644 --- a/Sources/RDReaderView/EPUBCore/RDEPUBWebViewDebug.swift +++ b/Sources/RDReaderView/EPUBCore/RDEPUBWebViewDebug.swift @@ -21,6 +21,30 @@ enum RDEPUBWebViewDebug { #endif }() + /// 详细日志模式是否启用(默认关闭,可通过 UserDefaults "RDEPUBWebViewVerboseEnabled" 开启) + /// 开启后 logMessage 将输出完整消息体,关闭时仅输出消息名、字段名和文本长度 + static var isVerboseEnabled: Bool = { + if let configured = UserDefaults.standard.object(forKey: "RDEPUBWebViewVerboseEnabled") as? Bool { + return configured + } + return false + }() + + /// 是否允许开启 inspectable。 + /// 默认关闭,仅在阅读器配置显式允许或 UserDefaults 覆盖时开启。 + static var isInspectableEnabled: Bool = { + if let configured = UserDefaults.standard.object(forKey: "RDEPUBInspectableWebViewsEnabled") as? Bool { + return configured + } + return false + }() + + /// 将阅读器配置同步到调试策略,保证默认安全策略由配置显式控制。 + static func applyDebugPolicy(inspectableEnabled: Bool, verboseLoggingEnabled: Bool) { + isInspectableEnabled = inspectableEnabled + isVerboseEnabled = verboseLoggingEnabled + } + /// 获取 WebView 的十六进制标识符(用于日志区分多个 WebView 实例) static func webViewID(_ webView: WKWebView?) -> String { guard let webView else { return "nil-webview" } @@ -51,10 +75,26 @@ enum RDEPUBWebViewDebug { log(scope, message: "webView=\(webViewID(webView)) js=\(action) \(details)") } - /// 输出 JS 消息接收日志(含消息名称和消息体) + /// 输出 JS 消息接收日志(默认仅输出字段名和文本长度,verbose 模式输出完整消息体) static func logMessage(_ scope: String, webView: WKWebView?, name: String, body: Any) { guard isEnabled else { return } - log(scope, message: "webView=\(webViewID(webView)) message=\(name) body=\(String(describing: body))") + if isVerboseEnabled { + log(scope, message: "webView=\(webViewID(webView)) message=\(name) body=\(String(describing: body))") + return + } + if let dict = body as? [String: Any] { + let keys = dict.keys.sorted().joined(separator: ",") + var meta = "keys=[\(keys)]" + if let text = dict["text"] as? String { + meta += " textLen=\(text.count)" + } + if let urlString = dict["url"] as? String ?? dict["href"] as? String { + meta += " url=\(summarizedURL(URL(string: urlString)))" + } + log(scope, message: "webView=\(webViewID(webView)) message=\(name) \(meta)") + } else { + log(scope, message: "webView=\(webViewID(webView)) message=\(name) type=\(Swift.type(of: body))") + } } /// 输出 URL Scheme 任务日志(含请求 URL、文件 URL、事件类型、错误信息) @@ -81,4 +121,4 @@ enum RDEPUBWebViewDebug { } return url.lastPathComponent.isEmpty ? path : url.lastPathComponent } -} \ No newline at end of file +} diff --git a/Sources/RDReaderView/EPUBCore/Resources/epub-bridge.js b/Sources/RDReaderView/EPUBCore/Resources/epub-bridge.js index 52219bc..612785b 100644 --- a/Sources/RDReaderView/EPUBCore/Resources/epub-bridge.js +++ b/Sources/RDReaderView/EPUBCore/Resources/epub-bridge.js @@ -1,4 +1,4 @@ -(function() { + (function() { if (window.RDReaderBridge) { return; } window.addEventListener('error', function(event) { try { @@ -470,7 +470,18 @@ setVisiblePageIndex(pageIndex); reportProgression(null); }, - scrollToLocation: function(location, fallbackPageIndex) { + scrollToLocation: function(location, fallbackPageIndex, targetRangeInfo) { + if (targetRangeInfo) { + var highlightRange = rangeFromInfo(targetRangeInfo); + var highlightRects = rectPayloadForRange(highlightRange, 0, 0); + if (highlightRects.length) { + var highlightRect = highlightRects[0]; + var highlightAbsoluteLeft = Math.max(0, highlightRect.x + currentPageOffset()); + setVisiblePageIndex(Math.max(0, Math.floor(highlightAbsoluteLeft / effectivePageStride()))); + reportProgression(location && location.fragment ? location.fragment : null); + return; + } + } if (location && location.fragment) { var resolvedTarget = fragmentTarget(location.fragment); if (resolvedTarget) { diff --git a/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBChapterTailNormalizer.swift b/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBChapterTailNormalizer.swift index 0425413..0a1fa24 100644 --- a/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBChapterTailNormalizer.swift +++ b/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBChapterTailNormalizer.swift @@ -20,7 +20,9 @@ struct RDEPUBChapterTailNormalizer { previous.diagnostics.append(note) compacted.append(previous) } else { + #if DEBUG print("[EPUB][Pagination] href=\(href) dropped leading/intermediate whitespace frame \(NSStringFromRange(frame.contentRange))") + #endif } continue } @@ -36,7 +38,9 @@ struct RDEPUBChapterTailNormalizer { previousFrame.diagnostics.append(note) normalized.append(previousFrame) } else { + #if DEBUG print("[EPUB][Pagination] href=\(href) dropped trailing frame \(NSStringFromRange(lastFrame.contentRange))") + #endif } } diff --git a/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextBookBuilder.swift b/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextBookBuilder.swift index e1a96b9..7497c8c 100644 --- a/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextBookBuilder.swift +++ b/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextBookBuilder.swift @@ -137,7 +137,9 @@ public final class RDEPUBTextBookBuilder { // 保存分页缓存(只缓存页范围和分页原因,不缓存富文本) cacheCoordinator.save(chapters: chapters, key: cacheKey) + #if DEBUG print(sampler.summary()) + #endif lastBuildPerformanceSamples = sampler.samples return book @@ -207,11 +209,15 @@ public final class RDEPUBTextBookBuilder { let plainText = rendered.attributedString.string.trimmingCharacters(in: .whitespacesAndNewlines) if item.href.lowercased().contains("cover") { + #if DEBUG print("[EPUB][Cover] rendered href=\(item.href) textLength=\(plainText.count) attrLength=\(rendered.attributedString.length) attachments=\(attachmentCount(in: rendered.attributedString))") + #endif } if shouldSkipChapter(item: item, content: rendered.attributedString, text: plainText) { if item.href.lowercased().contains("cover") { + #if DEBUG print("[EPUB][Cover] skipped href=\(item.href)") + #endif } return nil } @@ -301,7 +307,9 @@ public final class RDEPUBTextBookBuilder { : normalizedFrames if item.href.lowercased().contains("cover") { + #if DEBUG print("[EPUB][Cover] paginated href=\(item.href) pages=\(effectiveFrames.count) firstRange=\(effectiveFrames.first.map { NSStringFromRange($0.contentRange) } ?? "none")") + #endif } let chapterAttributedContent = content.copy() as! NSAttributedString diff --git a/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextBookCache.swift b/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextBookCache.swift index 2322f11..3760776 100644 --- a/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextBookCache.swift +++ b/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextBookCache.swift @@ -170,7 +170,9 @@ public final class RDEPUBTextBookCache { queue.sync { let fileURL = cacheDirectory.appendingPathComponent(key) guard FileManager.default.fileExists(atPath: fileURL.path) else { + #if DEBUG print("[Cache] load MISS key=\(key)") + #endif return nil } do { @@ -179,17 +181,23 @@ public final class RDEPUBTextBookCache { ofClass: PaginationCacheArchive.self, from: data ) else { + #if DEBUG print("[Cache] load MISS key=\(key) (unarchive returned nil)") + #endif return nil } var result: [String: RDEPUBTextChapterPaginationCache] = [:] for chapter in archive.chapters { result[chapter.href] = chapter.toCache() } + #if DEBUG print("[Cache] load HIT key=\(key) chapters=\(result.count)") + #endif return result } catch { + #if DEBUG print("[Cache] load MISS key=\(key) error=\(error)") + #endif return nil } } @@ -204,9 +212,13 @@ public final class RDEPUBTextBookCache { let bookArchive = PaginationCacheArchive(chapters: archives) let data = try NSKeyedArchiver.archivedData(withRootObject: bookArchive, requiringSecureCoding: true) try data.write(to: fileURL, options: .atomic) + #if DEBUG print("[Cache] save key=\(key) chapters=\(chapters.count)") + #endif } catch { + #if DEBUG print("[Cache] save FAILED key=\(key) error=\(error)") + #endif } } } @@ -223,7 +235,9 @@ public final class RDEPUBTextBookCache { for file in contents { try? fileManager.removeItem(at: file) } + #if DEBUG print("[Cache] invalidateAll") + #endif } } } diff --git a/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextPerformanceSampler.swift b/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextPerformanceSampler.swift index 917067e..fd4fba8 100644 --- a/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextPerformanceSampler.swift +++ b/Sources/RDReaderView/EPUBTextRendering/BuildPipeline/RDEPUBTextPerformanceSampler.swift @@ -51,7 +51,9 @@ public final class RDEPUBTextPerformanceSampler { /// 记录单个章节的性能采样,并输出日志 public func record(_ sample: RDEPUBTextPerformanceSample) { samples.append(sample) + #if DEBUG print("[PERF] \(sample.chapterHref): render=\(formatMS(sample.renderDuration)) paginate=\(formatMS(sample.paginateDuration)) pages=\(sample.pageCount) cache=\(sample.cacheHit ? "HIT" : "MISS")") + #endif } /// 生成性能汇总报告,包含总渲染/分页耗时和缓存命中率 diff --git a/Sources/RDReaderView/EPUBTextRendering/Pagination/RDEPUBPageBreakPolicy.swift b/Sources/RDReaderView/EPUBTextRendering/Pagination/RDEPUBPageBreakPolicy.swift index 77130c0..6fd371f 100644 --- a/Sources/RDReaderView/EPUBTextRendering/Pagination/RDEPUBPageBreakPolicy.swift +++ b/Sources/RDReaderView/EPUBTextRendering/Pagination/RDEPUBPageBreakPolicy.swift @@ -166,80 +166,6 @@ struct RDEPUBPageBreakPolicy { // MARK: - 语义边界查找 - /// 在指定范围内查找最优的语义分页点(pageBreakBefore / pageBreakAfter)。 - func preferredSemanticBoundary( - in range: NSRange, - minimumEnd: Int, - factory: RDEPUBCoreTextPageFrameFactory - ) -> (location: Int, trigger: String)? { - guard let safeRange = factory.clampedRange(range), safeRange.length > 0 else { - return nil - } - var boundary: (location: Int, trigger: String)? - attributedString.enumerateAttribute(.rdPageSemanticHints, in: safeRange) { value, attributeRange, stop in - guard let rawValue = value as? String else { return } - let hints = rawValue - .split(separator: ",") - .compactMap { RDEPUBTextSemanticHint(rawValue: String($0)) } - guard !hints.isEmpty else { return } - - if hints.contains(.pageBreakBefore), - attributeRange.location > safeRange.location, - attributeRange.location >= minimumEnd { - boundary = (attributeRange.location, RDEPUBTextSemanticHint.pageBreakBefore.rawValue) - stop.pointee = true - return - } - - let attributeEnd = attributeRange.location + attributeRange.length - if hints.contains(.pageBreakAfter), - attributeEnd > minimumEnd, - attributeEnd < safeRange.location + safeRange.length { - boundary = (attributeEnd, RDEPUBTextSemanticHint.pageBreakAfter.rawValue) - stop.pointee = true - return - } - } - return boundary - } - - /// 查找附件边界:只有块级附件才触发分页。 - func preferredAttachmentBoundary( - in range: NSRange, - minimumEnd: Int, - factory: RDEPUBCoreTextPageFrameFactory - ) -> Int? { - guard let safeRange = factory.clampedRange(range), safeRange.length > 0 else { - return nil - } - var boundary: Int? - attributedString.enumerateAttribute(.rdPageAttachmentKind, in: safeRange) { value, attributeRange, stop in - guard value != nil else { return } - - let location = attributeRange.location - let placement = factory.attachmentPlacement(at: location) - let blockKind = factory.blockKind(at: location) - - let isBlockLevelAttachment: Bool - switch placement { - case .centered: - isBlockLevelAttachment = true - case .inline, .baseline: - isBlockLevelAttachment = false - case nil: - isBlockLevelAttachment = blockKind == .attachment - } - guard isBlockLevelAttachment else { return } - - let boundaryRange = factory.blockRange(at: location) ?? factory.paragraphRange(containing: location) - if boundaryRange.location > safeRange.location, boundaryRange.location >= minimumEnd { - boundary = boundaryRange.location - stop.pointee = true - } - } - return boundary - } - /// 查找 pageRelate 跨页关联边界。 func preferredPageRelateBoundary( after range: NSRange, diff --git a/Sources/RDReaderView/EPUBTextRendering/RDEPUBChapterData.swift b/Sources/RDReaderView/EPUBTextRendering/RDEPUBChapterData.swift index 8efe6c3..b91d77a 100644 --- a/Sources/RDReaderView/EPUBTextRendering/RDEPUBChapterData.swift +++ b/Sources/RDReaderView/EPUBTextRendering/RDEPUBChapterData.swift @@ -174,12 +174,12 @@ public final class RDEPUBChapterData { /// 从搜索结果还原绝对字符范围 public func absoluteRange(for searchMatch: RDEPUBSearchMatch) -> NSRange? { + if let location = searchMatch.rangeLocation { + return NSRange(location: location, length: max(searchMatch.rangeLength, 1)) + } if let rangeAnchor = searchMatch.rangeAnchor { return indexTable.chapterRange(for: rangeAnchor) } - if let location = searchMatch.rangeLocation { - return NSRange(location: location, length: searchMatch.rangeLength) - } return nil } diff --git a/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextIndexTable.swift b/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextIndexTable.swift index 4a32cc5..8978b78 100644 --- a/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextIndexTable.swift +++ b/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextIndexTable.swift @@ -187,11 +187,7 @@ public struct RDEPUBTextIndexTable { /// 将锚点转换为章节内字符偏移量。 public func chapterOffset(for anchor: RDEPUBTextAnchor) -> Int { - chapterOffset( - fileIndex: anchor.fileIndex, - row: anchor.row, - column: anchor.column - ) ?? anchor.chapterOffset + anchor.chapterOffset } /// 将锚点转换为全书绝对字符索引。 diff --git a/Sources/RDReaderView/EPUBTextRendering/Typesetter/RDEPUBAttachmentNormalizer.swift b/Sources/RDReaderView/EPUBTextRendering/Typesetter/RDEPUBAttachmentNormalizer.swift index 96fa57d..e3053af 100644 --- a/Sources/RDReaderView/EPUBTextRendering/Typesetter/RDEPUBAttachmentNormalizer.swift +++ b/Sources/RDReaderView/EPUBTextRendering/Typesetter/RDEPUBAttachmentNormalizer.swift @@ -46,7 +46,9 @@ struct RDEPUBAttachmentNormalizer { if !didLogFootnoteAttachment { didLogFootnoteAttachment = true + #if DEBUG print("[EPUB][Attachment] footnote original=\(RDEPUBHTMLNormalizer.string(from: originalSize)) display=\(RDEPUBHTMLNormalizer.string(from: attachment.displaySize)) font=\(pointSize)") + #endif } return } @@ -66,7 +68,9 @@ struct RDEPUBAttachmentNormalizer { if !didLogCoverAttachment { didLogCoverAttachment = true + #if DEBUG print("[EPUB][Attachment] cover original=\(RDEPUBHTMLNormalizer.string(from: originalSize)) display=\(RDEPUBHTMLNormalizer.string(from: attachment.displaySize))") + #endif } return } diff --git a/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+ContentDelegates.swift b/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+ContentDelegates.swift index 553a82f..d8c113e 100644 --- a/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+ContentDelegates.swift +++ b/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+ContentDelegates.swift @@ -62,15 +62,17 @@ extension RDEPUBReaderController: RDEPUBWebContentViewDelegate { readerView.transitionToPage(pageNum: max(pageNumber - 1, 0), animated: true) } - /// Web 内容视图外部链接点击回调,使用系统浏览器打开 + /// Web 内容视图外部链接点击回调,根据配置策略决定是否打开 func epubWebContentView(_ contentView: RDEPUBWebContentView, didActivateExternalLink url: URL) { delegate?.epubReader(self, didActivateExternalLink: url) - UIApplication.shared.open(url, options: [:], completionHandler: nil) + openExternalURLIfAllowed(url) } /// Web 内容视图 JavaScript 错误日志回调 func epubWebContentView(_ contentView: RDEPUBWebContentView, didLogJavaScriptError message: String) { + #if DEBUG print("EPUB JS Error: \(message)") + #endif } } @@ -96,6 +98,14 @@ extension RDEPUBReaderController: RDEPUBTextContentViewDelegate { contentView.clearSelection() } + func textContentView( + _ contentView: RDEPUBTextContentView, + didActivateAttachmentText text: String, + sourceRect: CGRect + ) { + presentAttachmentTooltip(text: text, sourceView: contentView, sourceRect: sourceRect) + } + func textContentView( _ contentView: RDEPUBTextContentView, didRequestHighlightActions highlight: RDEPUBHighlight, @@ -296,4 +306,242 @@ extension RDEPUBReaderController: RDEPUBTextContentViewDelegate { } return bestID } + + // MARK: - 外部链接策略 + + private func shouldAllowExternalURL(_ url: URL) -> Bool { + guard let scheme = url.scheme?.lowercased() else { return false } + if delegate?.epubReader(self, shouldOpenExternalURL: url) == false { + return false + } + return configuration.allowedExternalURLSchemes.contains(scheme) + } + + private func openExternalURLIfAllowed(_ url: URL) { + guard shouldAllowExternalURL(url) else { return } + if configuration.requiresExternalLinkConfirmation { + presentExternalLinkConfirmation(for: url) + } else { + UIApplication.shared.open(url, options: [:], completionHandler: nil) + } + } + + private func presentExternalLinkConfirmation(for url: URL) { + let alert = UIAlertController( + title: "打开外部链接", + message: url.absoluteString, + preferredStyle: .alert + ) + alert.addAction(UIAlertAction(title: "取消", style: .cancel)) + alert.addAction(UIAlertAction(title: "打开", style: .default) { _ in + UIApplication.shared.open(url, options: [:], completionHandler: nil) + }) + present(alert, animated: true) + } + + private func presentAttachmentTooltip(text: String, sourceView: UIView, sourceRect: CGRect) { + hideAttachmentTooltipIfNeeded() + + let overlay = RDEPUBAttachmentTooltipOverlayView(frame: view.bounds) + overlay.autoresizingMask = [.flexibleWidth, .flexibleHeight] + overlay.onBackgroundTap = { [weak self, weak overlay] in + guard let self, let overlay else { return } + self.dismissAttachmentTooltipOverlay(overlay) + } + + let tooltip = RDEPUBAttachmentTooltipView() + tooltip.alpha = 0 + tooltip.configure(text: text, maxWidth: min(view.bounds.width - 48, 320)) + + let anchorRect = sourceView.convert(sourceRect, to: view) + let horizontalPadding: CGFloat = 24 + let verticalSpacing: CGFloat = 6 + let tooltipSize = tooltip.frame.size + let idealX = anchorRect.midX - tooltipSize.width / 2 + let minX = horizontalPadding + let maxX = max(minX, view.bounds.width - horizontalPadding - tooltipSize.width) + let originX = min(max(idealX, minX), maxX) + let originY = max(view.safeAreaInsets.top + 12, anchorRect.minY - tooltipSize.height - verticalSpacing) + let arrowTipX = min( + max(anchorRect.midX - originX, tooltip.minimumArrowX), + tooltipSize.width - tooltip.minimumArrowX + ) + + tooltip.setArrowTipX(arrowTipX) + tooltip.frame.origin = CGPoint(x: originX, y: originY) + overlay.addSubview(tooltip) + view.addSubview(overlay) + + UIView.animate(withDuration: 0.2) { + tooltip.alpha = 1 + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 3.5) { [weak self, weak overlay] in + guard let self, let overlay else { return } + self.dismissAttachmentTooltipOverlay(overlay) + } + } + + private func hideAttachmentTooltipIfNeeded() { + view.subviews + .compactMap { $0 as? RDEPUBAttachmentTooltipOverlayView } + .forEach { $0.removeFromSuperview() } + } + + private func dismissAttachmentTooltipOverlay(_ overlay: RDEPUBAttachmentTooltipOverlayView) { + UIView.animate(withDuration: 0.18, animations: { + overlay.tooltipView?.alpha = 0 + }, completion: { _ in + overlay.removeFromSuperview() + }) + } +} + +private final class RDEPUBAttachmentTooltipOverlayView: UIView { + var onBackgroundTap: (() -> Void)? + + var tooltipView: RDEPUBAttachmentTooltipView? { + subviews.compactMap { $0 as? RDEPUBAttachmentTooltipView }.first + } + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = .clear + + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:))) + tapGesture.cancelsTouchesInView = false + addGestureRecognizer(tapGesture) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc + private func handleTap(_ gesture: UITapGestureRecognizer) { + let point = gesture.location(in: self) + guard let tooltipView else { + onBackgroundTap?() + return + } + if !tooltipView.frame.contains(point) { + onBackgroundTap?() + } + } +} + +private final class RDEPUBAttachmentTooltipView: UIView { + private let contentInsets = UIEdgeInsets(top: 18, left: 20, bottom: 24, right: 20) + private let arrowSize = CGSize(width: 20, height: 10) + private let cornerRadius: CGFloat = 18 + private(set) var minimumArrowX: CGFloat = 28 + private var arrowTipX: CGFloat? + private let textLabel: UILabel = { + let label = UILabel() + label.numberOfLines = 0 + label.textColor = .white + label.font = .systemFont(ofSize: 16, weight: .regular) + label.lineBreakMode = .byWordWrapping + return label + }() + private let shapeLayer = CAShapeLayer() + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = .clear + isOpaque = false + layer.addSublayer(shapeLayer) + addSubview(textLabel) + accessibilityIdentifier = "epub.reader.attachment.tooltip" + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + shapeLayer.frame = bounds + shapeLayer.path = bubblePath(in: bounds).cgPath + shapeLayer.fillColor = UIColor(white: 0.26, alpha: 0.96).cgColor + + let labelFrame = bounds.inset(by: UIEdgeInsets( + top: contentInsets.top, + left: contentInsets.left, + bottom: contentInsets.bottom + arrowSize.height, + right: contentInsets.right + )) + textLabel.frame = labelFrame + } + + func setArrowTipX(_ value: CGFloat) { + arrowTipX = value + setNeedsLayout() + } + + func configure(text: String, maxWidth: CGFloat) { + textLabel.text = text + let labelMaxWidth = max(maxWidth - contentInsets.left - contentInsets.right, 120) + let labelSize = textLabel.sizeThatFits(CGSize(width: labelMaxWidth, height: .greatestFiniteMagnitude)) + frame.size = CGSize( + width: min(maxWidth, labelSize.width + contentInsets.left + contentInsets.right), + height: labelSize.height + contentInsets.top + contentInsets.bottom + arrowSize.height + ) + setNeedsLayout() + layoutIfNeeded() + } + + private func bubblePath(in rect: CGRect) -> UIBezierPath { + let bubbleRect = CGRect( + x: rect.minX, + y: rect.minY, + width: rect.width, + height: rect.height - arrowSize.height + ) + let arrowMidX = min( + max(arrowTipX ?? bubbleRect.midX, minimumArrowX), + bubbleRect.width - minimumArrowX + ) + let arrowHalfWidth = arrowSize.width / 2 + + let path = UIBezierPath() + path.move(to: CGPoint(x: bubbleRect.minX + cornerRadius, y: bubbleRect.minY)) + path.addLine(to: CGPoint(x: bubbleRect.maxX - cornerRadius, y: bubbleRect.minY)) + path.addArc( + withCenter: CGPoint(x: bubbleRect.maxX - cornerRadius, y: bubbleRect.minY + cornerRadius), + radius: cornerRadius, + startAngle: -.pi / 2, + endAngle: 0, + clockwise: true + ) + path.addLine(to: CGPoint(x: bubbleRect.maxX, y: bubbleRect.maxY - cornerRadius)) + path.addArc( + withCenter: CGPoint(x: bubbleRect.maxX - cornerRadius, y: bubbleRect.maxY - cornerRadius), + radius: cornerRadius, + startAngle: 0, + endAngle: .pi / 2, + clockwise: true + ) + path.addLine(to: CGPoint(x: arrowMidX + arrowHalfWidth, y: bubbleRect.maxY)) + path.addLine(to: CGPoint(x: arrowMidX, y: bubbleRect.maxY + arrowSize.height)) + path.addLine(to: CGPoint(x: arrowMidX - arrowHalfWidth, y: bubbleRect.maxY)) + path.addLine(to: CGPoint(x: bubbleRect.minX + cornerRadius, y: bubbleRect.maxY)) + path.addArc( + withCenter: CGPoint(x: bubbleRect.minX + cornerRadius, y: bubbleRect.maxY - cornerRadius), + radius: cornerRadius, + startAngle: .pi / 2, + endAngle: .pi, + clockwise: true + ) + path.addLine(to: CGPoint(x: bubbleRect.minX, y: bubbleRect.minY + cornerRadius)) + path.addArc( + withCenter: CGPoint(x: bubbleRect.minX + cornerRadius, y: bubbleRect.minY + cornerRadius), + radius: cornerRadius, + startAngle: .pi, + endAngle: -.pi / 2, + clockwise: true + ) + path.close() + return path + } } diff --git a/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+DataSource.swift b/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+DataSource.swift index 123336d..6c3faf8 100644 --- a/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+DataSource.swift +++ b/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+DataSource.swift @@ -97,16 +97,28 @@ extension RDEPUBReaderController: RDReaderDataSource, RDReaderDelegate { private func searchState(for page: RDEPUBTextPage) -> RDEPUBSearchState? { guard let globalSearchState = searchState else { return nil } - let matches = globalSearchState.matches.filter { searchMatch in - searchMatchBelongsToPage(searchMatch, page: page) + let matches: [RDEPUBSearchMatch] + let currentMatchIndex: Int? + + if let chapterData = chapterData(for: page), + let resolvedState = resolvedSearchState( + for: page, + chapterData: chapterData, + globalSearchState: globalSearchState + ) { + matches = resolvedState.matches + currentMatchIndex = resolvedState.currentMatchIndex + } else { + matches = globalSearchState.matches.filter { searchMatch in + searchMatchBelongsToPage(searchMatch, page: page) + } + currentMatchIndex = globalSearchState.currentMatch.flatMap { currentMatch in + matches.firstIndex(of: currentMatch) + } } guard !matches.isEmpty || globalSearchState.currentMatch != nil else { return globalSearchState.matches.isEmpty ? globalSearchState : nil } - - let currentMatchIndex = globalSearchState.currentMatch.flatMap { currentMatch in - matches.firstIndex(of: currentMatch) - } return RDEPUBSearchState( keyword: globalSearchState.keyword, matches: matches, @@ -114,6 +126,126 @@ extension RDEPUBReaderController: RDReaderDataSource, RDReaderDelegate { ) } + private func resolvedSearchState( + for page: RDEPUBTextPage, + chapterData: RDEPUBChapterData, + globalSearchState: RDEPUBSearchState + ) -> RDEPUBSearchState? { + let normalizedKeyword = globalSearchState.keyword.trimmingCharacters(in: .whitespacesAndNewlines) + guard !normalizedKeyword.isEmpty else { + return globalSearchState.matches.isEmpty ? globalSearchState : nil + } + + let normalizedHref = normalizedPageHref(for: page) + let exactMatches = exactChapterSearchMatches( + in: chapterData, + keyword: normalizedKeyword, + normalizedHref: normalizedHref + ) + let pageMatches = exactMatches.filter { match in + guard let rangeLocation = match.rangeLocation else { return false } + let range = NSRange(location: rangeLocation, length: max(match.rangeLength, 1)) + return NSIntersectionRange(range, page.contentRange).length > 0 + } + + let currentLocalMatchIndex = globalSearchState.currentMatch?.localMatchIndex + let currentMatchIndex = currentLocalMatchIndex.flatMap { localMatchIndex in + pageMatches.firstIndex(where: { $0.localMatchIndex == localMatchIndex }) + } + + guard !pageMatches.isEmpty || currentMatchIndex != nil else { + return globalSearchState.matches.isEmpty ? globalSearchState : nil + } + + return RDEPUBSearchState( + keyword: globalSearchState.keyword, + matches: pageMatches, + currentMatchIndex: currentMatchIndex + ) + } + + private func exactChapterSearchMatches( + in chapterData: RDEPUBChapterData, + keyword: String, + normalizedHref: String + ) -> [RDEPUBSearchMatch] { + let source = chapterData.attributedContent.string as NSString + let fullLength = source.length + guard fullLength > 0 else { return [] } + + var matches: [RDEPUBSearchMatch] = [] + var localMatchIndex = 0 + var searchRange = NSRange(location: 0, length: fullLength) + + while searchRange.length > 0 { + let foundRange = source.range(of: keyword, options: [.caseInsensitive], range: searchRange) + guard foundRange.location != NSNotFound else { break } + + let progressionDenominator = max(fullLength - 1, 1) + let progression = Double(foundRange.location) / Double(progressionDenominator) + matches.append( + RDEPUBSearchMatch( + href: normalizedHref, + progression: progression, + previewText: previewText(in: source, matchRange: foundRange), + localMatchIndex: localMatchIndex, + rangeLocation: foundRange.location, + rangeLength: max(foundRange.length, 1), + rangeAnchor: chapterData.rangeAnchor(for: foundRange) + ) + ) + + localMatchIndex += 1 + let nextLocation = foundRange.location + max(foundRange.length, 1) + if nextLocation >= fullLength { + break + } + searchRange = NSRange(location: nextLocation, length: fullLength - nextLocation) + } + + return matches + } + + private func previewText(in text: NSString, matchRange: NSRange) -> String { + let previewRadius = 12 + let start = max(matchRange.location - previewRadius, 0) + let end = min(matchRange.location + matchRange.length + previewRadius, text.length) + let range = NSRange(location: start, length: max(end - start, 0)) + return text.substring(with: range).trimmingCharacters(in: .whitespacesAndNewlines) + } + + private func chapterData(for page: RDEPUBTextPage) -> RDEPUBChapterData? { + if let textBook, + let chapterData = textBook.chapterData(for: page.href) { + return chapterData + } + + guard let runtimeChapter = runtime.chapterRuntimeStore.chapterData(for: page.spineIndex) else { + return nil + } + return makeChapterData(from: runtimeChapter, chapterIndex: page.chapterIndex) + } + + private func makeChapterData( + from runtimeChapter: RDEPUBRuntimeChapter, + chapterIndex: Int + ) -> RDEPUBChapterData { + let textChapter = RDEPUBTextChapter( + chapterIndex: chapterIndex, + spineIndex: runtimeChapter.spineIndex, + href: runtimeChapter.href, + title: runtimeChapter.title, + attributedContent: runtimeChapter.typesetAttributedString, + fragmentOffsets: runtimeChapter.chapterOffsetMap.fragmentOffsets, + pageBreakReasons: runtimeChapter.pages.map(\.metadata.breakReason), + pages: runtimeChapter.pages + ) + return RDEPUBChapterData( + chapter: textChapter, + indexTable: RDEPUBTextIndexTable(chapters: [textChapter]) + ) + } + private func searchMatchBelongsToPage(_ searchMatch: RDEPUBSearchMatch, page: RDEPUBTextPage) -> Bool { if let textBook, let chapterData = textBook.chapterData(for: page.href), diff --git a/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+RenderSupport.swift b/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+RenderSupport.swift index e32f2e5..797e26f 100644 --- a/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+RenderSupport.swift +++ b/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+RenderSupport.swift @@ -60,11 +60,16 @@ extension RDEPUBReaderController { } let page = activePages[pageIndex] let pendingLocation = readingSession?.pendingLocation(forPageNumber: pageIndex + 1, spineIndex: page.spineIndex) + let pendingHighlightRangeInfo = readingSession?.pendingHighlightRangeInfo( + forPageNumber: pageIndex + 1, + spineIndex: page.spineIndex + ) return currentPreferences().renderRequest( for: page, publication: publication, viewportSize: currentLayoutContext().viewportSize, targetLocation: pendingLocation, + targetHighlightRangeInfo: pendingHighlightRangeInfo, highlights: highlights(for: page), searchPresentation: searchPresentation(for: page) ) @@ -97,4 +102,3 @@ extension RDEPUBReaderController { } } } - diff --git a/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+RuntimeBridge.swift b/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+RuntimeBridge.swift index 689ea13..3bffba9 100644 --- a/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+RuntimeBridge.swift +++ b/Sources/RDReaderView/EPUBUI/RDEPUBReaderController+RuntimeBridge.swift @@ -91,8 +91,16 @@ extension RDEPUBReaderController { /// 恢复到指定阅读位置,返回是否成功 @discardableResult - func restoreReadingLocation(_ location: RDEPUBLocation, animated: Bool = false) -> Bool { - runtime.restoreReadingLocation(location, animated: animated) + func restoreReadingLocation( + _ location: RDEPUBLocation, + animated: Bool = false, + targetHighlightRangeInfo: String? = nil + ) -> Bool { + runtime.restoreReadingLocation( + location, + animated: animated, + targetHighlightRangeInfo: targetHighlightRangeInfo + ) } /// 获取当前可见页面的位置 diff --git a/Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift b/Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift index 4a14d3d..af5fbc9 100644 --- a/Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift +++ b/Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift @@ -30,6 +30,7 @@ public final class RDEPUBReaderController: UIViewController { public var configuration: RDEPUBReaderConfiguration { didSet { readerContext.configuration = configuration + applyWebViewDebugPolicy() persistReaderSettingsIfNeeded() guard isViewLoaded else { return } let oldConfiguration = oldValue @@ -230,12 +231,20 @@ public final class RDEPUBReaderController: UIViewController { readerContext.epubURL = epubURL readerContext.persistence = persistence self.currentBrightness = brightness + applyWebViewDebugPolicy() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + private func applyWebViewDebugPolicy() { + RDEPUBWebViewDebug.applyDebugPolicy( + inspectableEnabled: configuration.allowsInspectableWebViews, + verboseLoggingEnabled: configuration.enablesVerboseWebViewLogging + ) + } + /// 使用已构建的 TextBook 初始化,跳过 EPUB 解析流程 /// 适用于 TXT 等纯文本文件,调用方需自行构建 TextBook /// - Parameters: @@ -318,9 +327,21 @@ public final class RDEPUBReaderController: UIViewController { searchBarView.apply(theme: configuration.theme) searchBarView.onSearchSubmit = { [weak self] keyword in + self?.searchBarView.showSearching() self?.runtime.search(keyword: keyword) self?.updateSearchCount() } + searchBarView.onSearchTextChanged = { [weak self] keyword in + guard let self else { return } + let normalizedKeyword = keyword.trimmingCharacters(in: .whitespacesAndNewlines) + if normalizedKeyword.isEmpty { + self.runtime.clearSearch() + } else { + self.searchBarView.showSearching() + self.runtime.search(keyword: normalizedKeyword) + } + self.updateSearchCount() + } searchBarView.onSearchPrevious = { [weak self] in _ = self?.runtime.searchPrevious() self?.updateSearchCount() @@ -329,6 +350,14 @@ public final class RDEPUBReaderController: UIViewController { _ = self?.runtime.searchNext() self?.updateSearchCount() } + searchBarView.onSelectMatch = { [weak self] matchIndex in + guard let self else { return } + let didNavigate = self.runtime.selectSearchMatch(at: matchIndex) + self.updateSearchCount() + if didNavigate { + self.hideSearchBar(clearSearch: false) + } + } searchBarView.onClose = { [weak self] in self?.hideSearchBar(clearSearch: true) } @@ -345,22 +374,28 @@ public final class RDEPUBReaderController: UIViewController { readerView.addSubview(searchBarView) readerView.searchBarView = searchBarView - let topToolbarHeight: CGFloat = readerView.safeAreaInsets.top + 52 + let bottomAnchor = bottomToolView.superview == nil + ? readerView.bottomAnchor + : bottomToolView.topAnchor NSLayoutConstraint.activate([ searchBarView.leadingAnchor.constraint(equalTo: readerView.leadingAnchor), searchBarView.trailingAnchor.constraint(equalTo: readerView.trailingAnchor), - searchBarView.topAnchor.constraint(equalTo: readerView.topAnchor, constant: topToolbarHeight), - searchBarView.heightAnchor.constraint(equalToConstant: 52) + searchBarView.topAnchor.constraint(equalTo: readerView.topAnchor), + searchBarView.bottomAnchor.constraint(equalTo: bottomAnchor) ]) - searchBarView.transform = CGAffineTransform(translationX: 0, y: -52) - UIView.animate(withDuration: 0.3) { - self.searchBarView.transform = .identity + searchBarView.alpha = 0 + searchBarView.presentedView.transform = CGAffineTransform(translationX: 0, y: 40) + UIView.animate(withDuration: 0.28) { + self.searchBarView.alpha = 1 + self.searchBarView.presentedView.transform = .identity } if let keyword = searchState?.keyword, !keyword.isEmpty { searchBarView.restoreKeyword(keyword) updateSearchCount() + } else { + searchBarView.showNoResults() } DispatchQueue.main.async { [weak self] in @@ -375,11 +410,13 @@ public final class RDEPUBReaderController: UIViewController { isSearchBarVisible = false searchBarView.textField.resignFirstResponder() - UIView.animate(withDuration: 0.3, animations: { - self.searchBarView.transform = CGAffineTransform(translationX: 0, y: -52) + UIView.animate(withDuration: 0.25, animations: { + self.searchBarView.alpha = 0 + self.searchBarView.presentedView.transform = CGAffineTransform(translationX: 0, y: 40) }) { _ in self.searchBarView.removeFromSuperview() - self.searchBarView.transform = .identity + self.searchBarView.alpha = 1 + self.searchBarView.presentedView.transform = .identity self.readerView.searchBarView = nil } @@ -394,11 +431,11 @@ public final class RDEPUBReaderController: UIViewController { searchBarView.showNoResults() return } - if let index = searchState.currentMatchIndex { - searchBarView.updateMatchCount(current: index + 1, total: searchState.matches.count) - } else if searchState.matches.isEmpty { - searchBarView.showNoResults() - } + searchBarView.updateResults( + sections: searchResultSections(for: searchState), + keyword: searchState.keyword, + currentMatchIndex: searchState.currentMatchIndex + ) } /// 当工具栏可见性变化时同步搜索栏(由 RDReaderView 回调调用) @@ -417,3 +454,53 @@ public final class RDEPUBReaderController: UIViewController { } } + +private extension RDEPUBReaderController { + func searchResultSections(for searchState: RDEPUBSearchState) -> [RDEPUBReaderSearchSection] { + let groupedMatches = Dictionary(grouping: Array(searchState.matches.enumerated()), by: { entry in + searchSectionTitle(for: entry.element) + }) + + let orderedTitles = searchState.matches.reduce(into: [String]()) { titles, match in + let title = searchSectionTitle(for: match) + if titles.last != title, titles.contains(title) == false { + titles.append(title) + } + } + + return orderedTitles.compactMap { title in + guard let matches = groupedMatches[title] else { return nil } + let items = matches.map { offset, match in + RDEPUBReaderSearchSection.Item( + matchIndex: offset, + previewText: match.previewText, + isCurrent: offset == searchState.currentMatchIndex + ) + } + return RDEPUBReaderSearchSection(title: title, items: items) + } + } + + func searchSectionTitle(for match: RDEPUBSearchMatch) -> String { + guard let publication else { + return match.href + } + + let normalizedMatchHref = publication.resourceResolver.normalizedHref(match.href) ?? match.href + let tocItems = flattenedTableOfContentsItems(from: publication.tableOfContents, includePageNumbers: false) + if let tocItem = tocItems.last(where: { + let rawHref = $0.href.components(separatedBy: "#").first ?? $0.href + let normalizedItemHref = publication.resourceResolver.normalizedHref(rawHref) ?? rawHref + return normalizedItemHref == normalizedMatchHref + }) { + return tocItem.title + } + + if let spineIndex = publication.resourceResolver.spineIndex(forNormalizedHref: normalizedMatchHref), + publication.spine.indices.contains(spineIndex) { + return publication.spine[spineIndex].title + } + + return normalizedMatchHref + } +} diff --git a/Sources/RDReaderView/EPUBUI/RDEPUBReaderDelegate.swift b/Sources/RDReaderView/EPUBUI/RDEPUBReaderDelegate.swift index 3c5bdd6..d81ff3b 100644 --- a/Sources/RDReaderView/EPUBUI/RDEPUBReaderDelegate.swift +++ b/Sources/RDReaderView/EPUBUI/RDEPUBReaderDelegate.swift @@ -64,6 +64,13 @@ public protocol RDEPUBReaderDelegate: AnyObject { /// - url: 外部链接 URL func epubReader(_ reader: UIViewController, didActivateExternalLink url: URL) + /// 外部链接打开前的拦截钩子,返回 false 可阻止打开 + /// - Parameters: + /// - reader: 阅读器控制器 + /// - url: 外部链接 URL + /// - Returns: 是否允许打开该链接 + func epubReader(_ reader: UIViewController, shouldOpenExternalURL url: URL) -> Bool + /// 阅读器发生错误时调用 /// - Parameters: /// - reader: 阅读器控制器 @@ -92,6 +99,7 @@ public extension RDEPUBReaderDelegate { func epubReader(_ reader: UIViewController, didChangeCurrentSearchMatch match: RDEPUBSearchMatch?) {} func epubReader(_ reader: UIViewController, didUpdateCurrentTableOfContentsItem item: RDEPUBReaderTableOfContentsItem?) {} func epubReader(_ reader: UIViewController, didActivateExternalLink url: URL) {} + func epubReader(_ reader: UIViewController, shouldOpenExternalURL url: URL) -> Bool { true } func epubReader(_ reader: UIViewController, didFailWithError error: Error) {} func epubReader(_ reader: UIViewController, configureTopToolView topToolView: RDEPUBReaderTopToolView) {} } diff --git a/Sources/RDReaderView/EPUBUI/RDEPUBReaderPersistence.swift b/Sources/RDReaderView/EPUBUI/RDEPUBReaderPersistence.swift index c40043a..3cfc85d 100644 --- a/Sources/RDReaderView/EPUBUI/RDEPUBReaderPersistence.swift +++ b/Sources/RDReaderView/EPUBUI/RDEPUBReaderPersistence.swift @@ -39,23 +39,32 @@ public protocol RDEPUBReaderPersistence: AnyObject { // MARK: - 默认实现 /// 协议的默认空实现,书签/高亮/设置为可选功能 +/// DEBUG 模式下会对 no-op 行为输出警告,便于发现未对接持久化层的误用 public extension RDEPUBReaderPersistence { func loadBookmarks(for bookIdentifier: String) -> [RDEPUBBookmark] { - _ = bookIdentifier + #if DEBUG + print("[RDEPUBReaderPersistence] ⚠️ loadBookmarks called on default no-op implementation for: \(bookIdentifier)") + #endif return [] } func saveBookmarks(_ bookmarks: [RDEPUBBookmark], for bookIdentifier: String) { - _ = bookmarks - _ = bookIdentifier + #if DEBUG + print("[RDEPUBReaderPersistence] ⚠️ saveBookmarks(\(bookmarks.count) items) called on default no-op implementation for: \(bookIdentifier)") + #endif } func loadReaderSettings() -> RDEPUBReaderSettings? { - nil + #if DEBUG + print("[RDEPUBReaderPersistence] ⚠️ loadReaderSettings called on default no-op implementation") + #endif + return nil } func saveReaderSettings(_ settings: RDEPUBReaderSettings) { - _ = settings + #if DEBUG + print("[RDEPUBReaderPersistence] ⚠️ saveReaderSettings called on default no-op implementation") + #endif } } @@ -136,6 +145,11 @@ public final class RDEPUBUserDefaultsPersistence: RDEPUBReaderPersistence { guard let data = try? JSONEncoder().encode(highlights) else { return } + if data.count > 1_048_576 { + #if DEBUG + print("[RDEPUBUserDefaultsPersistence] ⚠️ saveHighlights data size (\(data.count) bytes) exceeds 1MB for: \(bookIdentifier)") + #endif + } defaults.set(data, forKey: highlightsPrefix + bookIdentifier) } diff --git a/Sources/RDReaderView/EPUBUI/RDEPUBReaderSearchBarView.swift b/Sources/RDReaderView/EPUBUI/RDEPUBReaderSearchBarView.swift index 18ffb20..220837a 100644 --- a/Sources/RDReaderView/EPUBUI/RDEPUBReaderSearchBarView.swift +++ b/Sources/RDReaderView/EPUBUI/RDEPUBReaderSearchBarView.swift @@ -1,47 +1,32 @@ import UIKit -// MARK: - 搜索栏 +struct RDEPUBReaderSearchSection: Equatable { + struct Item: Equatable { + let matchIndex: Int + let previewText: String + let isCurrent: Bool + } -/// 阅读器搜索栏视图 -/// 提供搜索输入、上一个/下一个匹配导航、匹配计数和关闭功能 + let title: String + let items: [Item] +} + +// MARK: - 搜索面板 + +/// 阅读器搜索面板 +/// 提供底部抽屉式搜索输入、分组结果列表和匹配跳转能力。 final class RDEPUBReaderSearchBarView: RDEPUBReaderToolView { - // MARK: 回调闭包 - - /// 提交搜索关键词回调 var onSearchSubmit: ((String) -> Void)? - /// 点击上一个匹配回调 + var onSearchTextChanged: ((String) -> Void)? var onSearchPrevious: (() -> Void)? - /// 点击下一个匹配回调 var onSearchNext: (() -> Void)? - /// 关闭搜索回调 + var onSelectMatch: ((Int) -> Void)? var onClose: (() -> Void)? - // MARK: UI 组件 - - private let containerView: UIView = { - let view = UIView() - view.layer.cornerRadius = 8 - view.layer.masksToBounds = true - view.isAccessibilityElement = false - view.accessibilityElementsHidden = false - return view - }() - - private let searchIcon: UIImageView = { - let imageView = UIImageView() - imageView.contentMode = .scaleAspectFit - imageView.preferredSymbolConfiguration = UIImage.SymbolConfiguration(pointSize: 14, weight: .medium) - if #available(iOS 13.0, *) { - imageView.image = UIImage(systemName: "magnifyingglass") - } - imageView.tintColor = .gray - return imageView - }() - let textField: UITextField = { let field = UITextField() - field.placeholder = "搜索..." - field.font = UIFont.systemFont(ofSize: 15) + field.placeholder = "搜索" + field.font = UIFont.systemFont(ofSize: 18, weight: .medium) field.returnKeyType = .search field.autocorrectionType = .no field.autocapitalizationType = .none @@ -53,25 +38,25 @@ final class RDEPUBReaderSearchBarView: RDEPUBReaderToolView { return field }() + private let backgroundButton = UIButton(type: .custom) + private let panelView = UIView() + private let grabberView = UIView() + private let searchRowView = UIView() + private let searchFieldContainer = UIView() + private let searchIcon = UIImageView() + private let searchFieldDivider = UIView() + private let cancelButton = UIButton(type: .system) + private let tableView = UITableView(frame: .zero, style: .plain) + private let emptyStateLabel = UILabel() + + // Preserve legacy accessibility hooks used by existing tests and demo logic. private let previousButton = RDEPUBReaderTintButton(type: .system) private let nextButton = RDEPUBReaderTintButton(type: .system) + private let countLabel = UILabel() - private let countLabel: UILabel = { - let label = UILabel() - label.font = UIFont.systemFont(ofSize: 13, weight: .medium) - label.textAlignment = .center - label.setContentHuggingPriority(.required, for: .horizontal) - label.setContentCompressionResistancePriority(.required, for: .horizontal) - return label - }() - - private let closeButton = RDEPUBReaderTintButton(type: .system) - - // MARK: 布局常量 - - private let horizontalInset: CGFloat = 12 - private let spacing: CGFloat = 6 - private let containerHeight: CGFloat = 36 + private var searchSections: [RDEPUBReaderSearchSection] = [] + private var keyword = "" + private var currentMatchIndex: Int? override init(frame: CGRect) { super.init(frame: frame) @@ -81,162 +66,331 @@ final class RDEPUBReaderSearchBarView: RDEPUBReaderToolView { setupSubviews() setupConstraints() setupActions() - updateNavigationEnabled(false) + updateLegacyNavigationEnabled(false) + showInitialState() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - // MARK: 布局 - override func lineFrame(in bounds: CGRect) -> CGRect { - CGRect(x: 0, y: bounds.height - 0.5, width: bounds.width, height: 0.5) + .zero } override func apply(theme: RDEPUBReaderTheme) { super.apply(theme: theme) - backgroundColor = theme.toolBackgroundColor - containerView.backgroundColor = theme.toolControlBorderUnselectColor - searchIcon.tintColor = theme.toolControlTextColor - textField.textColor = theme.toolControlTextColor + let isDarkBackground = theme.contentBackgroundColor.rd_searchIsDarkBackground + + let overlayColor = isDarkBackground + ? UIColor(white: 0.12, alpha: 0.92) + : UIColor(white: 0.08, alpha: 0.82) + let panelColor = isDarkBackground + ? UIColor(red: 0.18, green: 0.18, blue: 0.19, alpha: 1) + : UIColor(red: 0.15, green: 0.15, blue: 0.16, alpha: 1) + let rowColor = isDarkBackground + ? UIColor(white: 0.18, alpha: 1) + : UIColor(white: 0.14, alpha: 0.96) + let cardColor = isDarkBackground + ? UIColor(white: 0.12, alpha: 1) + : UIColor(white: 0.10, alpha: 0.98) + let activeCardColor = UIColor(red: 0.17, green: 0.28, blue: 0.38, alpha: 1) + let textColor = UIColor(white: 0.96, alpha: 1) + let secondaryTextColor = UIColor(white: 0.72, alpha: 1) + + backgroundColor = .clear + backgroundButton.backgroundColor = overlayColor + panelView.backgroundColor = panelColor + grabberView.backgroundColor = UIColor(white: 0.75, alpha: 0.7) + searchRowView.backgroundColor = rowColor + searchFieldContainer.backgroundColor = .clear + searchFieldDivider.backgroundColor = UIColor(white: 1, alpha: 0.12) + searchIcon.tintColor = secondaryTextColor + cancelButton.tintColor = textColor + cancelButton.setTitleColor(textColor, for: .normal) + textField.textColor = textColor + textField.tintColor = UIColor.systemBlue + textField.keyboardAppearance = isDarkBackground ? .dark : .default textField.attributedPlaceholder = NSAttributedString( - string: "搜索...", - attributes: [.foregroundColor: theme.toolControlTextColor.withAlphaComponent(0.5)] + string: "搜索", + attributes: [.foregroundColor: secondaryTextColor] ) - countLabel.textColor = theme.toolControlTextColor - previousButton.tintColor = theme.toolControlTextColor - nextButton.tintColor = theme.toolControlTextColor - closeButton.tintColor = theme.toolControlTextColor + + emptyStateLabel.textColor = secondaryTextColor + tableView.backgroundColor = .clear + tableView.separatorStyle = .none + + previousButton.tintColor = textColor + nextButton.tintColor = textColor + countLabel.textColor = textColor + countLabel.backgroundColor = .clear + + RDEPUBReaderSearchResultCell.cardBackgroundColor = cardColor + RDEPUBReaderSearchResultCell.activeCardBackgroundColor = activeCardColor + RDEPUBReaderSearchResultCell.primaryTextColor = textColor + RDEPUBReaderSearchResultCell.highlightTextColor = UIColor.systemBlue + RDEPUBReaderSearchResultCell.activeHighlightTextColor = UIColor(red: 0.40, green: 0.77, blue: 1, alpha: 1) + + tableView.reloadData() } - // MARK: 公开方法 + var presentedView: UIView { + panelView + } - /// 更新匹配计数显示 func updateMatchCount(current: Int, total: Int) { countLabel.text = "\(current)/\(total)" - updateNavigationEnabled(total > 0) + textField.accessibilityValue = "\(current)/\(total)" + updateLegacyNavigationEnabled(total > 0) } - /// 显示无结果状态 func showNoResults() { - countLabel.text = "0/0" - updateNavigationEnabled(false) + currentMatchIndex = nil + updateMatchCount(current: 0, total: 0) + tableView.isHidden = true + emptyStateLabel.isHidden = false + emptyStateLabel.text = keyword.isEmpty ? "输入关键词开始搜索" : "未找到相关内容" } - /// 显示搜索中状态 func showSearching() { - countLabel.text = "搜索中..." - updateNavigationEnabled(false) + updateMatchCount(current: 0, total: 0) + tableView.isHidden = true + emptyStateLabel.isHidden = false + emptyStateLabel.text = "搜索中..." } - /// 恢复已有的搜索关键词(搜索栏重新显示时) func restoreKeyword(_ keyword: String) { textField.text = keyword + self.keyword = keyword } - // MARK: 私有方法 + func updateResults( + sections: [RDEPUBReaderSearchSection], + keyword: String, + currentMatchIndex: Int? + ) { + self.keyword = keyword + self.searchSections = sections + self.currentMatchIndex = currentMatchIndex + + let total = sections.reduce(0) { $0 + $1.items.count } + if let currentMatchIndex, total > 0 { + updateMatchCount(current: currentMatchIndex + 1, total: total) + } else { + updateMatchCount(current: 0, total: total) + } + + if total == 0 { + showNoResults() + return + } + + emptyStateLabel.isHidden = true + tableView.isHidden = false + tableView.reloadData() + scrollToCurrentMatchIfNeeded() + } private func setupSubviews() { - addSubview(containerView) - containerView.addSubview(searchIcon) - containerView.addSubview(textField) + backgroundButton.translatesAutoresizingMaskIntoConstraints = false + panelView.translatesAutoresizingMaskIntoConstraints = false + grabberView.translatesAutoresizingMaskIntoConstraints = false + searchRowView.translatesAutoresizingMaskIntoConstraints = false + searchFieldContainer.translatesAutoresizingMaskIntoConstraints = false + searchIcon.translatesAutoresizingMaskIntoConstraints = false + textField.translatesAutoresizingMaskIntoConstraints = false + searchFieldDivider.translatesAutoresizingMaskIntoConstraints = false + cancelButton.translatesAutoresizingMaskIntoConstraints = false + tableView.translatesAutoresizingMaskIntoConstraints = false + emptyStateLabel.translatesAutoresizingMaskIntoConstraints = false + previousButton.translatesAutoresizingMaskIntoConstraints = false + nextButton.translatesAutoresizingMaskIntoConstraints = false + countLabel.translatesAutoresizingMaskIntoConstraints = false + + addSubview(backgroundButton) + addSubview(panelView) + + panelView.addSubview(grabberView) + panelView.addSubview(searchRowView) + panelView.addSubview(tableView) + panelView.addSubview(emptyStateLabel) + + searchRowView.addSubview(searchFieldContainer) + searchRowView.addSubview(searchFieldDivider) + searchRowView.addSubview(cancelButton) + searchFieldContainer.addSubview(searchIcon) + searchFieldContainer.addSubview(textField) + + // Legacy shims addSubview(previousButton) addSubview(nextButton) addSubview(countLabel) - addSubview(closeButton) if #available(iOS 13.0, *) { - previousButton.setImage(UIImage(systemName: "chevron.up")?.withRenderingMode(.alwaysTemplate), for: .normal) - nextButton.setImage(UIImage(systemName: "chevron.down")?.withRenderingMode(.alwaysTemplate), for: .normal) - closeButton.setImage(UIImage(systemName: "xmark")?.withRenderingMode(.alwaysTemplate), for: .normal) + searchIcon.image = UIImage(systemName: "magnifyingglass") + previousButton.setImage(UIImage(systemName: "chevron.up"), for: .normal) + nextButton.setImage(UIImage(systemName: "chevron.down"), for: .normal) } else { previousButton.setTitle("▲", for: .normal) nextButton.setTitle("▼", for: .normal) - closeButton.setTitle("✕", for: .normal) } + searchIcon.contentMode = .scaleAspectFit + searchIcon.preferredSymbolConfiguration = UIImage.SymbolConfiguration(pointSize: 22, weight: .regular) + + panelView.layer.cornerRadius = 28 + panelView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + panelView.clipsToBounds = true + + grabberView.layer.cornerRadius = 3 + searchRowView.layer.cornerRadius = 22 + searchFieldContainer.layer.cornerRadius = 22 + searchRowView.clipsToBounds = true + + cancelButton.setTitle("取消", for: .normal) + cancelButton.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .medium) + cancelButton.accessibilityIdentifier = "epub.reader.search.close" + + emptyStateLabel.font = UIFont.systemFont(ofSize: 17, weight: .medium) + emptyStateLabel.textAlignment = .center + emptyStateLabel.numberOfLines = 0 + previousButton.accessibilityIdentifier = "epub.reader.search.previous" nextButton.accessibilityIdentifier = "epub.reader.search.next" - closeButton.accessibilityIdentifier = "epub.reader.search.close" countLabel.accessibilityIdentifier = "epub.reader.search.count" textField.accessibilityIdentifier = "epub.reader.search.field" + previousButton.alpha = 0.01 + nextButton.alpha = 0.01 + countLabel.alpha = 0.01 - [previousButton, nextButton, closeButton].forEach { button in - button.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .medium) - button.tintColor = .black - button.setTitleColor(.black, for: .normal) - } + tableView.register(RDEPUBReaderSearchResultCell.self, forCellReuseIdentifier: RDEPUBReaderSearchResultCell.reuseIdentifier) + tableView.dataSource = self + tableView.delegate = self + tableView.showsVerticalScrollIndicator = false + tableView.contentInset = UIEdgeInsets(top: 4, left: 0, bottom: 16, right: 0) } private func setupConstraints() { - [containerView, searchIcon, textField, previousButton, nextButton, countLabel, closeButton].forEach { - $0.translatesAutoresizingMaskIntoConstraints = false - } - NSLayoutConstraint.activate([ - // 容器(搜索输入区域) - containerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horizontalInset), - containerView.centerYAnchor.constraint(equalTo: centerYAnchor), - containerView.heightAnchor.constraint(equalToConstant: containerHeight), + backgroundButton.leadingAnchor.constraint(equalTo: leadingAnchor), + backgroundButton.trailingAnchor.constraint(equalTo: trailingAnchor), + backgroundButton.topAnchor.constraint(equalTo: topAnchor), + backgroundButton.bottomAnchor.constraint(equalTo: bottomAnchor), - // 搜索图标 - searchIcon.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 10), - searchIcon.centerYAnchor.constraint(equalTo: containerView.centerYAnchor), - searchIcon.widthAnchor.constraint(equalToConstant: 16), + panelView.leadingAnchor.constraint(equalTo: leadingAnchor), + panelView.trailingAnchor.constraint(equalTo: trailingAnchor), + panelView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 8), + panelView.bottomAnchor.constraint(equalTo: bottomAnchor), - // 输入框 - textField.leadingAnchor.constraint(equalTo: searchIcon.trailingAnchor, constant: 6), - textField.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8), - textField.centerYAnchor.constraint(equalTo: containerView.centerYAnchor), - textField.heightAnchor.constraint(equalToConstant: containerHeight - 4), + grabberView.topAnchor.constraint(equalTo: panelView.topAnchor, constant: 10), + grabberView.centerXAnchor.constraint(equalTo: panelView.centerXAnchor), + grabberView.widthAnchor.constraint(equalToConstant: 92), + grabberView.heightAnchor.constraint(equalToConstant: 6), - // 上一个按钮 - previousButton.leadingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: spacing), - previousButton.centerYAnchor.constraint(equalTo: centerYAnchor), - previousButton.widthAnchor.constraint(equalToConstant: 32), - previousButton.heightAnchor.constraint(equalToConstant: 32), + searchRowView.leadingAnchor.constraint(equalTo: panelView.leadingAnchor, constant: 20), + searchRowView.trailingAnchor.constraint(equalTo: panelView.trailingAnchor, constant: -20), + searchRowView.topAnchor.constraint(equalTo: grabberView.bottomAnchor, constant: 18), + searchRowView.heightAnchor.constraint(equalToConstant: 52), - // 下一个按钮 - nextButton.leadingAnchor.constraint(equalTo: previousButton.trailingAnchor, constant: spacing), - nextButton.centerYAnchor.constraint(equalTo: centerYAnchor), - nextButton.widthAnchor.constraint(equalToConstant: 32), - nextButton.heightAnchor.constraint(equalToConstant: 32), + searchFieldContainer.leadingAnchor.constraint(equalTo: searchRowView.leadingAnchor, constant: 12), + searchFieldContainer.topAnchor.constraint(equalTo: searchRowView.topAnchor), + searchFieldContainer.bottomAnchor.constraint(equalTo: searchRowView.bottomAnchor), - // 计数标签 - countLabel.leadingAnchor.constraint(equalTo: nextButton.trailingAnchor, constant: spacing), - countLabel.centerYAnchor.constraint(equalTo: centerYAnchor), - countLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: 44), + searchIcon.leadingAnchor.constraint(equalTo: searchFieldContainer.leadingAnchor, constant: 10), + searchIcon.centerYAnchor.constraint(equalTo: searchFieldContainer.centerYAnchor), + searchIcon.widthAnchor.constraint(equalToConstant: 24), + searchIcon.heightAnchor.constraint(equalToConstant: 24), - // 关闭按钮 - closeButton.leadingAnchor.constraint(equalTo: countLabel.trailingAnchor, constant: spacing), - closeButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -horizontalInset), - closeButton.centerYAnchor.constraint(equalTo: centerYAnchor), - closeButton.widthAnchor.constraint(equalToConstant: 32), - closeButton.heightAnchor.constraint(equalToConstant: 32) + textField.leadingAnchor.constraint(equalTo: searchIcon.trailingAnchor, constant: 10), + textField.trailingAnchor.constraint(equalTo: searchFieldContainer.trailingAnchor, constant: -10), + textField.topAnchor.constraint(equalTo: searchFieldContainer.topAnchor), + textField.bottomAnchor.constraint(equalTo: searchFieldContainer.bottomAnchor), + + searchFieldDivider.leadingAnchor.constraint(equalTo: searchFieldContainer.trailingAnchor, constant: 12), + searchFieldDivider.centerYAnchor.constraint(equalTo: searchRowView.centerYAnchor), + searchFieldDivider.widthAnchor.constraint(equalToConstant: 1), + searchFieldDivider.heightAnchor.constraint(equalToConstant: 28), + + cancelButton.leadingAnchor.constraint(equalTo: searchFieldDivider.trailingAnchor, constant: 18), + cancelButton.trailingAnchor.constraint(equalTo: searchRowView.trailingAnchor, constant: -18), + cancelButton.centerYAnchor.constraint(equalTo: searchRowView.centerYAnchor), + + tableView.leadingAnchor.constraint(equalTo: panelView.leadingAnchor, constant: 0), + tableView.trailingAnchor.constraint(equalTo: panelView.trailingAnchor, constant: 0), + tableView.topAnchor.constraint(equalTo: searchRowView.bottomAnchor, constant: 18), + tableView.bottomAnchor.constraint(equalTo: panelView.safeAreaLayoutGuide.bottomAnchor), + + emptyStateLabel.leadingAnchor.constraint(equalTo: panelView.leadingAnchor, constant: 32), + emptyStateLabel.trailingAnchor.constraint(equalTo: panelView.trailingAnchor, constant: -32), + emptyStateLabel.topAnchor.constraint(equalTo: searchRowView.bottomAnchor, constant: 56), + + previousButton.topAnchor.constraint(equalTo: topAnchor), + previousButton.leadingAnchor.constraint(equalTo: leadingAnchor), + previousButton.widthAnchor.constraint(equalToConstant: 1), + previousButton.heightAnchor.constraint(equalToConstant: 1), + + nextButton.topAnchor.constraint(equalTo: topAnchor), + nextButton.leadingAnchor.constraint(equalTo: previousButton.trailingAnchor), + nextButton.widthAnchor.constraint(equalToConstant: 1), + nextButton.heightAnchor.constraint(equalToConstant: 1), + + countLabel.topAnchor.constraint(equalTo: topAnchor), + countLabel.leadingAnchor.constraint(equalTo: nextButton.trailingAnchor), + countLabel.widthAnchor.constraint(equalToConstant: 1), + countLabel.heightAnchor.constraint(equalToConstant: 1) ]) } private func setupActions() { + textField.delegate = self textField.addTarget(self, action: #selector(textFieldDidReturn), for: .editingDidEndOnExit) + textField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged) previousButton.addTarget(self, action: #selector(previousAction), for: .touchUpInside) nextButton.addTarget(self, action: #selector(nextAction), for: .touchUpInside) - closeButton.addTarget(self, action: #selector(closeAction), for: .touchUpInside) + cancelButton.addTarget(self, action: #selector(closeAction), for: .touchUpInside) + backgroundButton.addTarget(self, action: #selector(closeAction), for: .touchUpInside) } - private func updateNavigationEnabled(_ enabled: Bool) { + private func updateLegacyNavigationEnabled(_ enabled: Bool) { previousButton.isEnabled = enabled - previousButton.alpha = enabled ? 1 : 0.45 nextButton.isEnabled = enabled - nextButton.alpha = enabled ? 1 : 0.45 + } + + private func showInitialState() { + tableView.isHidden = true + emptyStateLabel.isHidden = false + emptyStateLabel.text = "输入关键词开始搜索" + } + + private func scrollToCurrentMatchIfNeeded() { + guard let currentMatchIndex else { return } + for (sectionIndex, section) in searchSections.enumerated() { + if let rowIndex = section.items.firstIndex(where: { $0.matchIndex == currentMatchIndex }) { + let indexPath = IndexPath(row: rowIndex, section: sectionIndex) + DispatchQueue.main.async { [weak self] in + self?.tableView.scrollToRow(at: indexPath, at: .middle, animated: false) + } + return + } + } + } + + private func item(at indexPath: IndexPath) -> RDEPUBReaderSearchSection.Item { + searchSections[indexPath.section].items[indexPath.row] } @objc private func textFieldDidReturn() { - guard let keyword = textField.text, !keyword.isEmpty else { return } + let keyword = textField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" + guard !keyword.isEmpty else { return } onSearchSubmit?(keyword) textField.resignFirstResponder() } + @objc private func textFieldDidChange() { + guard textField.markedTextRange == nil else { return } + onSearchTextChanged?(textField.text ?? "") + } + @objc private func previousAction() { onSearchPrevious?() } @@ -249,3 +403,197 @@ final class RDEPUBReaderSearchBarView: RDEPUBReaderToolView { onClose?() } } + +private extension UIColor { + var rd_searchIsDarkBackground: Bool { + var red: CGFloat = 0 + var green: CGFloat = 0 + var blue: CGFloat = 0 + var alpha: CGFloat = 0 + guard getRed(&red, green: &green, blue: &blue, alpha: &alpha) else { + return false + } + let luminance = (0.299 * red) + (0.587 * green) + (0.114 * blue) + return luminance < 0.5 + } +} + +extension RDEPUBReaderSearchBarView: UITableViewDataSource, UITableViewDelegate { + func numberOfSections(in tableView: UITableView) -> Int { + searchSections.count + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + searchSections[section].items.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell( + withIdentifier: RDEPUBReaderSearchResultCell.reuseIdentifier, + for: indexPath + ) + + guard let cell = cell as? RDEPUBReaderSearchResultCell else { + return cell + } + + let item = item(at: indexPath) + cell.configure( + previewText: item.previewText, + keyword: keyword, + isCurrent: item.isCurrent + ) + cell.accessibilityIdentifier = "epub.reader.search.result.\(item.matchIndex)" + return cell + } + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + let container = UIView() + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.font = UIFont.systemFont(ofSize: 19, weight: .bold) + label.textColor = UIColor(white: 0.96, alpha: 1) + label.text = searchSections[section].title + label.numberOfLines = 2 + container.addSubview(label) + + NSLayoutConstraint.activate([ + label.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 20), + label.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -20), + label.topAnchor.constraint(equalTo: container.topAnchor, constant: 4), + label.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -4) + ]) + return container + } + + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + 40 + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + UITableView.automaticDimension + } + + func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { + 116 + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + onSelectMatch?(item(at: indexPath).matchIndex) + } +} + +extension RDEPUBReaderSearchBarView: UITextFieldDelegate { + func textFieldShouldClear(_ textField: UITextField) -> Bool { + DispatchQueue.main.async { [weak self] in + self?.onSearchTextChanged?("") + } + return true + } +} + +private final class RDEPUBReaderSearchResultCell: UITableViewCell { + static let reuseIdentifier = "RDEPUBReaderSearchResultCell" + + static var cardBackgroundColor = UIColor(white: 0.12, alpha: 1) + static var activeCardBackgroundColor = UIColor(red: 0.17, green: 0.28, blue: 0.38, alpha: 1) + static var primaryTextColor = UIColor(white: 0.96, alpha: 1) + static var highlightTextColor = UIColor.systemBlue + static var activeHighlightTextColor = UIColor(red: 0.40, green: 0.77, blue: 1, alpha: 1) + + private let cardView = UIView() + private let previewLabel = UILabel() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setupSubviews() + setupConstraints() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func prepareForReuse() { + super.prepareForReuse() + previewLabel.attributedText = nil + } + + func configure(previewText: String, keyword: String, isCurrent: Bool) { + selectionStyle = .none + backgroundColor = .clear + contentView.backgroundColor = .clear + cardView.backgroundColor = isCurrent ? Self.activeCardBackgroundColor : Self.cardBackgroundColor + previewLabel.attributedText = attributedPreviewText( + previewText, + keyword: keyword, + isCurrent: isCurrent + ) + } + + private func setupSubviews() { + cardView.translatesAutoresizingMaskIntoConstraints = false + previewLabel.translatesAutoresizingMaskIntoConstraints = false + + contentView.addSubview(cardView) + cardView.addSubview(previewLabel) + + cardView.layer.cornerRadius = 16 + cardView.clipsToBounds = true + + previewLabel.numberOfLines = 0 + previewLabel.font = UIFont.systemFont(ofSize: 18, weight: .regular) + } + + private func setupConstraints() { + NSLayoutConstraint.activate([ + cardView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20), + cardView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), + cardView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10), + cardView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10), + + previewLabel.leadingAnchor.constraint(equalTo: cardView.leadingAnchor, constant: 16), + previewLabel.trailingAnchor.constraint(equalTo: cardView.trailingAnchor, constant: -16), + previewLabel.topAnchor.constraint(equalTo: cardView.topAnchor, constant: 16), + previewLabel.bottomAnchor.constraint(equalTo: cardView.bottomAnchor, constant: -16) + ]) + } + + private func attributedPreviewText(_ previewText: String, keyword: String, isCurrent: Bool) -> NSAttributedString { + let normalizedText = previewText + .replacingOccurrences(of: "\n", with: " ") + .replacingOccurrences(of: "\\s+", with: " ", options: .regularExpression) + .trimmingCharacters(in: .whitespacesAndNewlines) + + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineSpacing = 8 + + let attributed = NSMutableAttributedString( + string: normalizedText, + attributes: [ + .font: UIFont.systemFont(ofSize: 18, weight: .regular), + .foregroundColor: Self.primaryTextColor, + .paragraphStyle: paragraphStyle + ] + ) + + let searchKeyword = keyword.trimmingCharacters(in: .whitespacesAndNewlines) + guard !searchKeyword.isEmpty else { return attributed } + + let nsText = normalizedText as NSString + var searchRange = NSRange(location: 0, length: nsText.length) + let highlightColor = isCurrent ? Self.activeHighlightTextColor : Self.highlightTextColor + + while searchRange.length > 0 { + let foundRange = nsText.range(of: searchKeyword, options: [.caseInsensitive], range: searchRange) + guard foundRange.location != NSNotFound else { break } + attributed.addAttribute(.foregroundColor, value: highlightColor, range: foundRange) + let nextLocation = foundRange.location + max(foundRange.length, 1) + guard nextLocation < nsText.length else { break } + searchRange = NSRange(location: nextLocation, length: nsText.length - nextLocation) + } + + return attributed + } +} diff --git a/Sources/RDReaderView/EPUBUI/RDEPUBReaderTopToolView.swift b/Sources/RDReaderView/EPUBUI/RDEPUBReaderTopToolView.swift index 25a8487..e2f42f8 100644 --- a/Sources/RDReaderView/EPUBUI/RDEPUBReaderTopToolView.swift +++ b/Sources/RDReaderView/EPUBUI/RDEPUBReaderTopToolView.swift @@ -136,5 +136,6 @@ public final class RDEPUBReaderTopToolView: RDEPUBReaderToolView { } else { bookmarkButton.setTitle(isBookmarked ? "已签" : "书签", for: .normal) } + bookmarkButton.accessibilityValue = isBookmarked ? "selected" : "unselected" } } diff --git a/Sources/RDReaderView/EPUBUI/RDURLReaderController.swift b/Sources/RDReaderView/EPUBUI/RDURLReaderController.swift index 56bd42e..22fb315 100644 --- a/Sources/RDReaderView/EPUBUI/RDURLReaderController.swift +++ b/Sources/RDReaderView/EPUBUI/RDURLReaderController.swift @@ -54,6 +54,9 @@ public final class RDURLReaderController: UIViewController { private var demoStateTimer: Timer? private var lastEmittedDemoState = "" private var pendingSearchKeyword: String? + private var externalLinkActivationCount = 0 + private var lastActivatedExternalURL: URL? + private var lastReaderErrorDescription = "none" /// 初始化方法 /// - Parameters: @@ -78,6 +81,7 @@ public final class RDURLReaderController: UIViewController { super.viewDidLoad() view.backgroundColor = .systemBackground title = bookURL.deletingPathExtension().lastPathComponent + RDEPUBResourceURLSchemeHandler.resetDebugMetrics() embedReaderController() installDemoStateLabel() } @@ -370,6 +374,10 @@ public final class RDURLReaderController: UIViewController { let progression = location.map { String(format: "%.4f", $0.navigationProgression) } ?? "nil" let mapSnapshot = demoPaginationSnapshot() let layoutConfig = readerController?.readerContext.currentTextLayoutConfig(pageSize: currentTextPageSize()) + let resourceMetrics = RDEPUBResourceURLSchemeHandler.debugMetricsSnapshot() + let cacheStats = readerController?.readerContext.makeChapterSummaryDiskCache().cacheStatistics + ?? (fileCount: 0, totalBytes: 0) + let inspectable = readerController?.configuration.allowsInspectableWebViews ?? epubConfiguration.allowsInspectableWebViews let state = [ "reader=opened", "page=\(page)", @@ -389,7 +397,17 @@ public final class RDURLReaderController: UIViewController { "avoidOrphans=\(layoutConfig?.avoidOrphans == true ? 1 : 0)", "windowSize=\(readerController?.configuration.onDemandChapterWindowSize ?? epubConfiguration.onDemandChapterWindowSize)", "parseMs=\(readerController?.readerContext.lastMetadataParseWallClockMs ?? 0)", - "parseConcurrency=\(readerController?.readerContext.lastMetadataParseConcurrency ?? 0)" + "parseConcurrency=\(readerController?.readerContext.lastMetadataParseConcurrency ?? 0)", + "inspectable=\(inspectable ? 1 : 0)", + "streamedResources=\(resourceMetrics.streamedResponses)", + "inMemoryResources=\(resourceMetrics.inMemoryResponses)", + "resourceFailures=\(resourceMetrics.failures)", + "cacheFiles=\(cacheStats.fileCount)", + "cacheBytes=\(cacheStats.totalBytes)", + "externalLinks=\(externalLinkActivationCount)", + "lastExternalURL=\(encodedDemoLocationHref(lastActivatedExternalURL?.absoluteString))", + "lastError=\(encodedDemoField(lastReaderErrorDescription))", + "searchMatchText=\(encodedDemoField(currentSearchMatchText()))" ].joined(separator: " ") demoStateLabel.text = state if let logPrefix { @@ -405,6 +423,25 @@ public final class RDURLReaderController: UIViewController { return href.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? href.replacingOccurrences(of: " ", with: "%20") } + private func encodedDemoField(_ value: String?) -> String { + guard let value, !value.isEmpty else { return "nil" } + return value.addingPercentEncoding(withAllowedCharacters: .alphanumerics) ?? value.replacingOccurrences(of: " ", with: "_") + } + + private func currentSearchMatchText() -> String { + guard let match = readerController?.searchState?.currentMatch, + let rangeLocation = match.rangeLocation, + let chapterData = readerController?.textChapterData(forNormalizedHref: match.href) else { + return "none" + } + let nsRange = NSRange(location: rangeLocation, length: match.rangeLength) + guard nsRange.location >= 0, + nsRange.location + nsRange.length <= chapterData.attributedContent.length else { + return "none" + } + return chapterData.attributedContent.attributedSubstring(from: nsRange).string + } + private func demoPaginationSnapshot() -> (mode: String, phase: String, knownPages: Int, knownChapters: Int, buildableChapters: Int) { guard let readerController else { return ("unavailable", "none", 0, 0, 0) @@ -486,6 +523,17 @@ extension RDURLReaderController: RDEPUBReaderDelegate { public func epubReader(_ reader: UIViewController, didUpdateBookmarks bookmarks: [RDEPUBBookmark]) { emitDemoState(prefix: "bookmarks=\(bookmarks.count)") } + + public func epubReader(_ reader: UIViewController, didActivateExternalLink url: URL) { + externalLinkActivationCount += 1 + lastActivatedExternalURL = url + emitDemoState(prefix: "externalLinks=\(externalLinkActivationCount)") + } + + public func epubReader(_ reader: UIViewController, didFailWithError error: Error) { + lastReaderErrorDescription = String(describing: error) + emitDemoState(prefix: "lastError=\(lastReaderErrorDescription)") + } } /// 翻页模式扩展:提供 Demo 命令行参数值 diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterSummaryDiskCache.swift b/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterSummaryDiskCache.swift index 61b4a0d..2e110bc 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterSummaryDiskCache.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterSummaryDiskCache.swift @@ -34,8 +34,28 @@ final class RDEPUBChapterSummaryDiskCache { func read(for key: RDEPUBChapterCacheKey) -> RDEPUBChapterSummary? { let fileURL = self.fileURL(for: key) - guard let data = try? Data(contentsOf: fileURL) else { return nil } - return try? JSONDecoder().decode(RDEPUBChapterSummary.self, from: data) + let data: Data + do { + data = try Data(contentsOf: fileURL) + } catch { + let nsError = error as NSError + if nsError.domain == NSCocoaErrorDomain && nsError.code == NSFileReadNoSuchFileError { + // 文件不存在属于正常缓存未命中,不报错 + } else { + #if DEBUG + print("[RDEPUBChapterSummaryDiskCache] ⚠️ read IO error for \(fileURL.lastPathComponent): \(error.localizedDescription)") + #endif + } + return nil + } + do { + return try JSONDecoder().decode(RDEPUBChapterSummary.self, from: data) + } catch { + #if DEBUG + print("[RDEPUBChapterSummaryDiskCache] ⚠️ decode error for \(fileURL.lastPathComponent): \(error.localizedDescription)") + #endif + return nil + } } // MARK: - 批量读取:二次打开时直接从磁盘构建 BookPageMap @@ -75,32 +95,78 @@ final class RDEPUBChapterSummaryDiskCache { return true } - /// 语义化别名:用于判断当前 renderSignature 下是否具备完整章节摘要集合。 - func containsCompleteSet(keys: [RDEPUBChapterCacheKey]) -> Bool { - isCacheComplete(keys: keys) - } - /// 清空所有缓存文件 func removeAll() { - guard let files = try? fileManager.contentsOfDirectory(at: cacheDirectory, includingPropertiesForKeys: nil) else { return } - for fileURL in files where fileURL.pathExtension == "json" { - try? fileManager.removeItem(at: fileURL) + removeFiles(matching: { _ in true }) + } + + /// 清空指定书籍的所有缓存文件 + func removeAll(forBookID bookID: String) { + let bookPrefix = Self.cacheNamespacePrefix(for: bookID) + removeFiles { $0.hasPrefix(bookPrefix + "__") } + } + + /// 清空指定渲染签名下的所有缓存文件 + func removeAll(forRenderSignature renderSignature: String) { + let renderPrefix = "__" + Self.cacheNamespacePrefix(for: renderSignature) + "__" + removeFiles { $0.contains(renderPrefix) } + } + + /// 缓存统计信息 + var cacheStatistics: (fileCount: Int, totalBytes: Int64) { + guard let files = try? fileManager.contentsOfDirectory(at: cacheDirectory, includingPropertiesForKeys: [.fileSizeKey]) else { + return (0, 0) } + var count = 0 + var totalBytes: Int64 = 0 + for fileURL in files where fileURL.pathExtension == "json" { + count += 1 + if let size = try? fileURL.resourceValues(forKeys: [.fileSizeKey]).fileSize { + totalBytes += Int64(size) + } + } + return (count, totalBytes) } // MARK: - key -> 文件路径 /// 使用确定性字符串拼接生成文件名,不依赖 Hashable.hashValue private func fileURL(for key: RDEPUBChapterCacheKey) -> URL { + let bookPrefix = Self.cacheNamespacePrefix(for: key.bookID) + let renderPrefix = Self.cacheNamespacePrefix(for: key.renderSignature) let rawKey = "\(key.bookID)_\(key.spineIndex)_\(key.renderSignature)_\(key.chapterContentHash)" let digest = rawKey.sha256Hex - return cacheDirectory.appendingPathComponent("\(digest).json") + return cacheDirectory.appendingPathComponent("\(bookPrefix)__\(renderPrefix)__\(digest).json") } private func writeImmediately(summary: RDEPUBChapterSummary, for key: RDEPUBChapterCacheKey) { let fileURL = self.fileURL(for: key) - let data = try? JSONEncoder().encode(summary) - try? data?.write(to: fileURL) + let tmpURL = fileURL.appendingPathExtension("tmp") + do { + let data = try JSONEncoder().encode(summary) + try data.write(to: tmpURL) + if fileManager.fileExists(atPath: fileURL.path) { + _ = try fileManager.replaceItemAt(fileURL, withItemAt: tmpURL) + } else { + try fileManager.moveItem(at: tmpURL, to: fileURL) + } + } catch { + #if DEBUG + print("[RDEPUBChapterSummaryDiskCache] ⚠️ write error for \(fileURL.lastPathComponent): \(error.localizedDescription)") + #endif + try? fileManager.removeItem(at: tmpURL) + } + } + + private func removeFiles(matching predicate: (String) -> Bool) { + guard let files = try? fileManager.contentsOfDirectory(at: cacheDirectory, includingPropertiesForKeys: nil) else { return } + for fileURL in files where fileURL.pathExtension == "json" && predicate(fileURL.lastPathComponent) { + try? fileManager.removeItem(at: fileURL) + } + } + + private static func cacheNamespacePrefix(for rawValue: String) -> String { + rawValue.sha256Hex.prefix(12).lowercased() } } diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterWindowCoordinator.swift b/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterWindowCoordinator.swift index bdc9d7c..a8aacdc 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterWindowCoordinator.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterWindowCoordinator.swift @@ -52,7 +52,9 @@ final class RDEPUBChapterWindowCoordinator { self.isSwitchingChapter = false self.buildSnapshotAroundCurrent(chapter: chapter) case .failure(let error): + #if DEBUG print("[EPUB][WindowCoord] loadChapter failed at spine=\(initialSpineIndex): \(error), trying next") + #endif // 自动跳过不可渲染的章节(封面/版权页等 linear=false 的 spine 项) let nextIndex = initialSpineIndex + 1 if nextIndex < totalSpineCount { @@ -76,7 +78,9 @@ final class RDEPUBChapterWindowCoordinator { private func buildSnapshotAroundCurrent(chapter: RDEPUBRuntimeChapter) { guard let current = store.currentSpineIndex else { + #if DEBUG print("[EPUB][WindowCoord] buildSnapshot: currentSpineIndex is nil, ABORT") + #endif return } let chapters = store.windowSpineIndices.compactMap { spineIndex -> RDEPUBRuntimeChapter? in @@ -86,7 +90,9 @@ final class RDEPUBChapterWindowCoordinator { return store.chapterData(for: spineIndex) } let snapshot = RDEPUBChapterWindowSnapshot.from(chapters: chapters, anchorSpineIndex: current) + #if DEBUG print("[EPUB][WindowCoord] snapshot: chapters=\(snapshot.chapters.count) pages=\(snapshot.pageCount) anchorPage=\(snapshot.anchorPageOffset)") + #endif currentSnapshot = snapshot isApplyingSnapshot = true onSnapshotChanged?(snapshot) @@ -240,14 +246,18 @@ final class RDEPUBChapterWindowCoordinator { private func handle(error: Error) { // 日志记录,不中断当前阅读状态 + #if DEBUG print("[RDEPUBChapterWindowCoordinator] chapter load error: \(error)") + #endif // 确保 loading 指示器在加载失败时也被隐藏(避免永久白屏) DispatchQueue.main.async { [weak self] in guard let self else { return } self.context.hideLoading() // 如果有快照但没内容显示,显示错误提示 if self.currentSnapshot == nil { + #if DEBUG print("[RDEPUBChapterWindowCoordinator] No snapshot after error, page will be blank") + #endif } } } diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBLocationConverter.swift b/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBLocationConverter.swift deleted file mode 100644 index 6522b0e..0000000 --- a/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBLocationConverter.swift +++ /dev/null @@ -1,111 +0,0 @@ -import Foundation - -struct RDEPUBLocationConverter { - - // MARK: - 主路径:先构建目标章,拿到真实长度后再精确转换 - - /// 旧版 RDEPUBLocation -> 新版 RDEPUBChapterLocation - /// 主迁移路径:要求先构建目标章,用真实 chapterLength 做精确转换 - /// 仅在无法获取章节长度时才降级到粗估 fallback - static func convert( - legacy location: RDEPUBLocation, - parser: RDEPUBParser, - publication: RDEPUBPublication, - chapterLengthProvider: ((Int) -> Int?)? = nil - ) -> RDEPUBChapterLocation? { - // 1. 从 href 找到 spineIndex - guard let spineItem = publication.spine.first(where: { - $0.href == location.href || $0.href.contains(location.href) - }) else { return nil } - - let spineIndex = publication.spine.firstIndex(of: spineItem) ?? 0 - - // 2. 优先用 fragmentID 定位(最精确,不受 progression 精度影响) - if let fragmentID = location.fragment { - return RDEPUBChapterLocation( - spineIndex: spineIndex, - chapterOffset: 0, // fragmentID 由 chapterOffsetMap 精确解析 - fragmentID: fragmentID, - progressionInChapter: location.progression - ) - } - - // 3. 有 chapterLength 时做精确转换 - if let provider = chapterLengthProvider, - let chapterLength = provider(spineIndex), chapterLength > 0 { - return convert( - legacy: location, - spineIndex: spineIndex, - chapterLength: chapterLength - ) - } - - // 4. Fallback:无法获取章节长度时的粗估(仅作临时降级) - let estimatedOffset = Int(location.progression * 10000) - return RDEPUBChapterLocation( - spineIndex: spineIndex, - chapterOffset: estimatedOffset, - fragmentID: nil, - progressionInChapter: location.progression, - schemaVersion: 1 // 标记为降级结果,后续可被精确值覆盖 - ) - } - - /// 精确转换:已知章节实际长度 - static func convert( - legacy location: RDEPUBLocation, - spineIndex: Int, - chapterLength: Int - ) -> RDEPUBChapterLocation? { - let offset = Int(location.progression * Double(chapterLength)) - return RDEPUBChapterLocation( - spineIndex: spineIndex, - chapterOffset: offset, - fragmentID: location.fragment, - progressionInChapter: location.progression, - schemaVersion: 2 - ) - } - - /// 从已构建的 RDEPUBRuntimeChapter 做精确转换(推荐迁移路径) - static func convert( - legacy location: RDEPUBLocation, - chapter: RDEPUBRuntimeChapter - ) -> RDEPUBChapterLocation? { - // 优先用 fragmentID - if let fragmentID = location.fragment, - let fragmentOffset = chapter.chapterOffsetMap.chapterOffset(forFragmentID: fragmentID) { - return RDEPUBChapterLocation( - spineIndex: chapter.spineIndex, - chapterOffset: fragmentOffset, - fragmentID: fragmentID, - progressionInChapter: nil, - schemaVersion: 2 - ) - } - - // 用 progression + 真实长度 - let chapterLength = chapter.typesetAttributedString.length - return convert( - legacy: location, - spineIndex: chapter.spineIndex, - chapterLength: chapterLength - ) - } - - /// 新版 -> 旧版(兼容外部接口) - static func toLegacy( - chapterLocation: RDEPUBChapterLocation, - href: String, - chapterLength: Int - ) -> RDEPUBLocation { - let progression = chapterLength > 0 - ? Double(chapterLocation.chapterOffset) / Double(chapterLength) - : 0 - return RDEPUBLocation( - href: href, - progression: min(max(progression, 0), 1), - fragment: chapterLocation.fragmentID - ) - } -} \ No newline at end of file diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBPageCountCache.swift b/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBPageCountCache.swift index cd889c0..bbb6cb9 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBPageCountCache.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBPageCountCache.swift @@ -17,12 +17,6 @@ final class RDEPUBPageCountCache { } } - func entriesForSpineIndex(_ spineIndex: Int) -> [(RDEPUBChapterCacheKey, RDEPUBRuntimePageCount)] { - lock.lock() - defer { lock.unlock() } - return storage.filter { $0.value.spineIndex == spineIndex }.map { ($0.key, $0.value) } - } - func remove(forSpineIndex spineIndex: Int) { lock.lock() defer { lock.unlock() } diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderAnnotationCoordinator.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderAnnotationCoordinator.swift index 10203e8..42015d6 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderAnnotationCoordinator.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderAnnotationCoordinator.swift @@ -32,14 +32,34 @@ final class RDEPUBReaderAnnotationCoordinator { /// 更新当前文本选区状态,并同步刷新底部工具栏的高亮按钮可用性。 func updateCurrentSelection(_ selection: RDEPUBSelection?) { - guard let controller else { return } - controller.currentSelection = selection?.isEmpty == false ? selection : nil - if controller.currentSelection != nil, - controller.readerView.isShowToolView == false { - controller.readerView.tapCenter() + if let selection, !selection.isEmpty { + applySelectionState(.selected(selection)) + } else { + applySelectionState(.idle) + } + } + + /// 统一选区状态变更入口。 + /// 在 `.selected` 时:更新 context 选区、显示工具栏、刷新 chrome、通知 delegate。 + /// 在 `.idle` 时:清空选区、刷新 chrome、通知 delegate。 + func applySelectionState(_ state: RDEPUBSelectionState) { + guard let controller else { return } + context.selectionState = state + switch state { + case .idle: + controller.updateReaderChrome() + controller.delegate?.epubReader(controller, didChangeSelection: nil) + case .selecting: + break + case .selected(let selection): + if controller.readerView.isShowToolView == false { + controller.readerView.tapCenter() + } + controller.updateReaderChrome() + controller.delegate?.epubReader(controller, didChangeSelection: selection) + case .committingAction: + break } - controller.updateReaderChrome() - controller.delegate?.epubReader(controller, didChangeSelection: controller.currentSelection) } /// 基于当前选区添加高亮标记,自动去重并持久化。 @@ -139,11 +159,10 @@ final class RDEPUBReaderAnnotationCoordinator { /// 跳转到指定高亮所在位置。 @discardableResult func go(toHighlightID id: String, animated: Bool = true) -> Bool { - guard let controller else { return false } guard let highlight = highlight(withID: id) else { return false } - return controller.restoreReadingLocation(highlight.location, animated: animated) + return navigate(to: highlight, animated: animated) } /// 清除所有高亮标记。 @@ -196,9 +215,8 @@ final class RDEPUBReaderAnnotationCoordinator { } ) highlightsController.onSelectHighlight = { [weak self, weak highlightsController] highlight in - guard let controller = self?.controller else { return } highlightsController?.dismiss(animated: true) { - controller.go(to: highlight.location) + _ = self?.navigate(to: highlight, animated: true) } } highlightsController.onUpdateHighlight = { [weak self] highlight in @@ -371,6 +389,17 @@ final class RDEPUBReaderAnnotationCoordinator { ) } + @discardableResult + private func navigate(to highlight: RDEPUBHighlight, animated: Bool) -> Bool { + guard let controller else { return false } + let navigationTarget = scopedHighlight(highlight) ?? highlight + return controller.restoreReadingLocation( + navigationTarget.location, + animated: animated, + targetHighlightRangeInfo: navigationTarget.rangeInfo + ) + } + private func persistHighlightsAndRefreshContent() { guard let controller else { return } if let currentBookIdentifier = controller.currentBookIdentifier { diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderAssemblyCoordinator.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderAssemblyCoordinator.swift index 82267cf..de865a9 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderAssemblyCoordinator.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderAssemblyCoordinator.swift @@ -23,7 +23,9 @@ final class RDEPUBReaderAssemblyCoordinator { setupLoadingIndicator(controller.loadingIndicator, in: controller.view) setupErrorLabel(controller.errorLabel, in: controller.view) controller.delegate?.epubReader(controller, configureTopToolView: controller.topToolView) + #if DEBUG print("[ReadViewDemo] assembleInterface: dataSource=\(readerView.dataSource != nil ? "set" : "nil"), numberOfPages=\(readerView.numberOfPages())") + #endif } /// 外部纯文本图书启动时,加载已保存的书签、高亮和阅读位置,完成分页收尾。 @@ -41,9 +43,13 @@ final class RDEPUBReaderAssemblyCoordinator { } if let textBook = controller.textBook { + #if DEBUG print("[ReadViewDemo] finishExternalTextBook: applying textBook with \(textBook.pages.count) pages") + #endif runtime.applyTextBook(textBook, restoreLocation: restoreLocation) + #if DEBUG print("[ReadViewDemo] finishExternalTextBook: after applyTextBook, numberOfPages=\(context.readerView?.numberOfPages() ?? -1)") + #endif } else { runtime.finishPagination(restoreLocation: restoreLocation) } diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderChromeCoordinator.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderChromeCoordinator.swift index 4dccc7c..e92d755 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderChromeCoordinator.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderChromeCoordinator.swift @@ -70,7 +70,7 @@ final class RDEPUBReaderChromeCoordinator { canToggleBookmark: controller.currentBookIdentifier != nil, hasBookmarkAtCurrentLocation: hasBookmarkAtCurrentLocation(), canShowBookmarks: !controller.activeBookmarks.isEmpty, - canAddHighlight: controller.configuration.allowsHighlights && controller.currentSelection != nil, + canAddHighlight: controller.configuration.allowsHighlights && context.selectionState.hasSelection, canShowHighlights: controller.configuration.allowsHighlights && !controller.activeHighlights.isEmpty, showsTableOfContents: controller.configuration.showsTableOfContents, allowsHighlights: controller.configuration.allowsHighlights, diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderContext.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderContext.swift index 37a1774..0a06319 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderContext.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderContext.swift @@ -59,8 +59,19 @@ final class RDEPUBReaderContext { var lastMetadataParseWallClockMs: Int = 0 /// 后台元数据解析使用的并发数。 var lastMetadataParseConcurrency: Int = 0 - /// 当前用户文本选区。 - var currentSelection: RDEPUBSelection? + /// 当前用户文本选区(对外只读语义,底层由 selectionState 推导)。 + var currentSelection: RDEPUBSelection? { + get { selectionState.selection } + set { + if let newValue, !newValue.isEmpty { + selectionState = .selected(newValue) + } else { + selectionState = .idle + } + } + } + /// 统一选区状态模型,收口所有选区相关状态变更。 + var selectionState: RDEPUBSelectionState = .idle // MARK: - 控制器状态(从 controller 下沉) diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderLocationCoordinator.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderLocationCoordinator.swift index 97e2c3c..a160d7a 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderLocationCoordinator.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderLocationCoordinator.swift @@ -16,7 +16,11 @@ final class RDEPUBReaderLocationCoordinator { /// 恢复到指定阅读位置,返回是否成功跳转。 @discardableResult - func restoreReadingLocation(_ location: RDEPUBLocation, animated: Bool = false) -> Bool { + func restoreReadingLocation( + _ location: RDEPUBLocation, + animated: Bool = false, + targetHighlightRangeInfo: String? = nil + ) -> Bool { guard let controller = context.controller, let readerView = context.readerView else { return false } guard let targetPageNumber = controller.pageNumber(for: location) else { @@ -32,13 +36,15 @@ final class RDEPUBReaderLocationCoordinator { _ = context.readingSession?.queueNavigation( to: location, relativeToSpineIndex: nil, - bookIdentifier: context.currentBookIdentifier + bookIdentifier: context.currentBookIdentifier, + targetHighlightRangeInfo: targetHighlightRangeInfo ) } else if context.textBook == nil { _ = context.readingSession?.queueNavigation( to: location, relativeToSpineIndex: nil, - bookIdentifier: context.currentBookIdentifier + bookIdentifier: context.currentBookIdentifier, + targetHighlightRangeInfo: targetHighlightRangeInfo ) } else { context.readingSession?.transition(to: .jumping) diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderPaginationCoordinator.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderPaginationCoordinator.swift index 56e34d1..aed7c7a 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderPaginationCoordinator.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderPaginationCoordinator.swift @@ -33,10 +33,14 @@ final class RDEPUBReaderPaginationCoordinator { controller.showLoading() let token = UUID() context.paginationToken = token + #if DEBUG print("[EPUB][Pagination] profile=\(publication.readingProfile.rawValue) layout=\(publication.layout.rawValue) spine=\(publication.spine.count)") + #endif if publication.readingProfile == .textReflowable { + #if DEBUG print("[EPUB][Pagination] path=text-reflowable-on-demand") + #endif paginateTextPublication( parser: parser, publication: publication, @@ -48,7 +52,9 @@ final class RDEPUBReaderPaginationCoordinator { } if publication.layout == .fixed { + #if DEBUG print("[EPUB][Pagination] path=fixed-layout") + #endif let snapshot = readingSession.makePaginationSnapshot( pageCounts: Array(repeating: 1, count: publication.spine.count), preferences: controller.currentPreferences(), @@ -59,7 +65,9 @@ final class RDEPUBReaderPaginationCoordinator { } let paginator = context.makePaginator() + #if DEBUG print("[EPUB][Pagination] path=web-paginator") + #endif context.paginator = paginator paginator.calculate( parser: parser, diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderRuntime.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderRuntime.swift index b72a232..cbeec3b 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderRuntime.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderRuntime.swift @@ -230,6 +230,12 @@ final class RDEPUBReaderRuntime { searchCoordinator.searchPrevious() } + /// 跳转到指定搜索匹配项 + @discardableResult + func selectSearchMatch(at index: Int) -> Bool { + searchCoordinator.selectSearchMatch(at: index) + } + /// 清除搜索状态 func clearSearch() { searchCoordinator.clearSearch() @@ -365,8 +371,16 @@ final class RDEPUBReaderRuntime { /// 恢复到指定阅读位置 @discardableResult - func restoreReadingLocation(_ location: RDEPUBLocation, animated: Bool = false) -> Bool { - locationCoordinator.restoreReadingLocation(location, animated: animated) + func restoreReadingLocation( + _ location: RDEPUBLocation, + animated: Bool = false, + targetHighlightRangeInfo: String? = nil + ) -> Bool { + locationCoordinator.restoreReadingLocation( + location, + animated: animated, + targetHighlightRangeInfo: targetHighlightRangeInfo + ) } /// 获取当前可见页面的阅读位置 diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderSearchCoordinator.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderSearchCoordinator.swift index f4a882b..6a40690 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderSearchCoordinator.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBReaderSearchCoordinator.swift @@ -51,6 +51,21 @@ final class RDEPUBReaderSearchCoordinator { advanceSearch(by: -1) } + /// 跳转到指定索引的搜索匹配项 + @discardableResult + func selectSearchMatch(at index: Int) -> Bool { + guard let controller else { return false } + guard var searchState = controller.searchState, + searchState.matches.indices.contains(index) else { + return false + } + + searchState.currentMatchIndex = index + controller.searchState = searchState + notifySearchStateChanged() + return navigateToCurrentSearchMatch(animated: true) + } + /// 清除搜索状态并刷新当前可见内容 func clearSearch() { guard let controller else { return } @@ -109,12 +124,109 @@ final class RDEPUBReaderSearchCoordinator { } return RDEPUBTextSearchEngine.searchWithoutPublication(textBook: textBook, keyword: keyword) } + if controller.readerContext.bookPageMap != nil, controller.publication != nil { + return resolvedOnDemandSearchMatches(for: keyword) + } if let parser = controller.parser, let publication = controller.publication { return RDEPUBHTMLSearchEngine(parser: parser, publication: publication).search(keyword: keyword) } return [] } + private func resolvedOnDemandSearchMatches(for keyword: String) -> [RDEPUBSearchMatch] { + guard let controller, + let publication = controller.publication else { + return [] + } + + let normalizedKeyword = keyword.trimmingCharacters(in: .whitespacesAndNewlines) + guard !normalizedKeyword.isEmpty else { + return [] + } + + let buildableSpineIndices = publication.spine.indices.filter { index in + let item = publication.spine[index] + return item.linear && (item.mediaType.contains("html") || item.mediaType.contains("xhtml")) + } + + var matches: [RDEPUBSearchMatch] = [] + for spineIndex in buildableSpineIndices { + guard let chapter = try? controller.runtime.chapterLoader.loadChapterSynchronouslyForMigration( + spineIndex: spineIndex, + store: controller.runtime.chapterRuntimeStore + ) else { + continue + } + + let chapterData = makeChapterData(from: chapter, chapterIndex: chapter.pages.first?.chapterIndex ?? 0) + let source = chapter.typesetAttributedString.string as NSString + let fullLength = source.length + guard fullLength > 0 else { continue } + + let normalizedHref = publication.resourceResolver.normalizedHref(chapter.href) ?? chapter.href + var localMatchIndex = 0 + var searchRange = NSRange(location: 0, length: fullLength) + + while searchRange.length > 0 { + let foundRange = source.range(of: normalizedKeyword, options: [.caseInsensitive], range: searchRange) + guard foundRange.location != NSNotFound else { + break + } + + let progressionDenominator = max(fullLength - 1, 1) + let progression = Double(foundRange.location) / Double(progressionDenominator) + matches.append( + RDEPUBSearchMatch( + href: normalizedHref, + progression: progression, + previewText: previewText(in: source, matchRange: foundRange), + localMatchIndex: localMatchIndex, + rangeLocation: foundRange.location, + rangeLength: foundRange.length, + rangeAnchor: chapterData.rangeAnchor(for: foundRange) + ) + ) + + localMatchIndex += 1 + let nextLocation = foundRange.location + max(foundRange.length, 1) + if nextLocation >= fullLength { + break + } + searchRange = NSRange(location: nextLocation, length: fullLength - nextLocation) + } + } + + return matches + } + + private func makeChapterData( + from runtimeChapter: RDEPUBRuntimeChapter, + chapterIndex: Int + ) -> RDEPUBChapterData { + let textChapter = RDEPUBTextChapter( + chapterIndex: chapterIndex, + spineIndex: runtimeChapter.spineIndex, + href: runtimeChapter.href, + title: runtimeChapter.title, + attributedContent: runtimeChapter.typesetAttributedString, + fragmentOffsets: runtimeChapter.chapterOffsetMap.fragmentOffsets, + pageBreakReasons: runtimeChapter.pages.map(\.metadata.breakReason), + pages: runtimeChapter.pages + ) + return RDEPUBChapterData( + chapter: textChapter, + indexTable: RDEPUBTextIndexTable(chapters: [textChapter]) + ) + } + + private func previewText(in text: NSString, matchRange: NSRange) -> String { + let previewRadius = 12 + let start = max(matchRange.location - previewRadius, 0) + let end = min(matchRange.location + matchRange.length + previewRadius, text.length) + let range = NSRange(location: start, length: max(end - start, 0)) + return text.substring(with: range).trimmingCharacters(in: .whitespacesAndNewlines) + } + private func advanceSearch(by delta: Int) -> Bool { guard let controller else { return false } guard var searchState = controller.searchState, !searchState.matches.isEmpty else { @@ -162,6 +274,14 @@ final class RDEPUBReaderSearchCoordinator { private func pageNumber(for searchMatch: RDEPUBSearchMatch) -> Int? { guard let controller else { return nil } if let chapterData = controller.textChapterData(forNormalizedHref: searchMatch.href) { + if let exactPageNumber = exactPageNumber( + for: searchMatch, + in: chapterData, + keyword: controller.searchState?.keyword + ) { + return exactPageNumber + } + if let pageNumber = chapterData.pageNumber(for: searchMatch) { return pageNumber } @@ -194,4 +314,39 @@ final class RDEPUBReaderSearchCoordinator { bookIdentifier: controller.currentBookIdentifier ).map { $0 + 1 } } + + private func exactPageNumber( + for searchMatch: RDEPUBSearchMatch, + in chapterData: RDEPUBChapterData, + keyword: String? + ) -> Int? { + let normalizedKeyword = keyword?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" + guard !normalizedKeyword.isEmpty else { return nil } + + let source = chapterData.attributedContent.string as NSString + let fullLength = source.length + guard fullLength > 0 else { return nil } + + var localMatchIndex = 0 + var searchRange = NSRange(location: 0, length: fullLength) + + while searchRange.length > 0 { + let foundRange = source.range(of: normalizedKeyword, options: [.caseInsensitive], range: searchRange) + guard foundRange.location != NSNotFound else { break } + + if localMatchIndex == searchMatch.localMatchIndex, + let page = chapterData.page(containing: foundRange.location) { + return page.absolutePageIndex + 1 + } + + localMatchIndex += 1 + let nextLocation = foundRange.location + max(foundRange.length, 1) + if nextLocation >= fullLength { + break + } + searchRange = NSRange(location: nextLocation, length: fullLength - nextLocation) + } + + return nil + } } diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBSelectionState.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBSelectionState.swift new file mode 100644 index 0000000..4aa79d1 --- /dev/null +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBSelectionState.swift @@ -0,0 +1,39 @@ +// RDEPUBSelectionState.swift +// 统一选区状态模型 +// 收口选区相关状态的冗余表达,降低 view 层与 controller 层各持有一份选区状态 +// 所带来的维护成本和时序问题。 + +import Foundation + +/// 统一选区状态枚举 +/// 替代原先散落在 view/controller/coordinator 的 `currentSelection != nil` 判断 +enum RDEPUBSelectionState: Equatable { + /// 无选区 + case idle + /// 用户正在拖拽选区(长按手势已开始,尚未松手) + case selecting(anchor: Int) + /// 选区已完成(用户松手,有有效文本) + case selected(RDEPUBSelection) + /// 正在执行选区菜单动作(拷贝/高亮/批注),动作完成后回到 idle + case committingAction(RDEPUBSelection, action: RDEPUBAnnotationMenuAction) + + /// 当前是否有有效选区(selecting / selected / committingAction 均视为有选区) + var hasSelection: Bool { + switch self { + case .idle: + return false + case .selecting, .selected, .committingAction: + return true + } + } + + /// 当前选区数据(如有) + var selection: RDEPUBSelection? { + switch self { + case .idle, .selecting: + return nil + case .selected(let selection), .committingAction(let selection, _): + return selection + } + } +} diff --git a/Sources/RDReaderView/EPUBUI/Settings/RDEPUBReaderConfiguration.swift b/Sources/RDReaderView/EPUBUI/Settings/RDEPUBReaderConfiguration.swift index c5c748b..c682679 100644 --- a/Sources/RDReaderView/EPUBUI/Settings/RDEPUBReaderConfiguration.swift +++ b/Sources/RDReaderView/EPUBUI/Settings/RDEPUBReaderConfiguration.swift @@ -114,6 +114,17 @@ public struct RDEPUBReaderConfiguration: Equatable { /// 若 writeTotalMs 占比显著(I/O 等待),可试探 cpuCount * 1.25~1.5 以填充 I/O 等待间隙。 public var metadataParsingConcurrency: Int + // MARK: 安全策略 + + /// 允许直接打开的外部 URL scheme 集合,默认仅允许 https + public var allowedExternalURLSchemes: Set + /// 打开外部链接前是否需要用户确认,默认 true + public var requiresExternalLinkConfirmation: Bool + /// 是否允许正文和离屏分页 WebView 开启 inspectable,默认 false + public var allowsInspectableWebViews: Bool + /// 是否允许输出完整 WebView 消息体日志,默认 false + public var enablesVerboseWebViewLogging: Bool + // MARK: 初始化 /// 创建阅读器配置,所有参数均提供合理的默认值 @@ -136,6 +147,10 @@ public struct RDEPUBReaderConfiguration: Equatable { /// - textRenderingEngine: 文本渲染引擎 /// - onDemandChapterWindowSize: 章节按需加载窗口大小(总章节数 3...15,偶数自动向上取奇) /// - metadataParsingConcurrency: 后台元数据解析并发数,默认 CPU 核心数 + /// - allowedExternalURLSchemes: 允许直接打开的外部 URL scheme 集合,默认仅 https + /// - requiresExternalLinkConfirmation: 打开外部链接前是否需要确认,默认 true + /// - allowsInspectableWebViews: 是否允许开启 inspectable,默认 false + /// - enablesVerboseWebViewLogging: 是否允许输出完整 WebView 消息体日志,默认 false public init( fontSize: CGFloat = 15, lineHeightMultiple: CGFloat = 1.6, @@ -156,7 +171,11 @@ public struct RDEPUBReaderConfiguration: Equatable { fixedLayoutSpreadMode: RDEPUBFixedLayoutSpreadMode = .automatic, textRenderingEngine: RDEPUBTextRenderingEngine = .dtCoreText, onDemandChapterWindowSize: Int = 3, - metadataParsingConcurrency: Int = ProcessInfo.processInfo.activeProcessorCount + metadataParsingConcurrency: Int = ProcessInfo.processInfo.activeProcessorCount, + allowedExternalURLSchemes: Set = ["https"], + requiresExternalLinkConfirmation: Bool = true, + allowsInspectableWebViews: Bool = false, + enablesVerboseWebViewLogging: Bool = false ) { self.fontSize = fontSize self.lineHeightMultiple = lineHeightMultiple @@ -178,6 +197,10 @@ public struct RDEPUBReaderConfiguration: Equatable { self.textRenderingEngine = textRenderingEngine self.onDemandChapterWindowSize = Self.normalizedChapterWindowSize(onDemandChapterWindowSize) self.metadataParsingConcurrency = max(1, metadataParsingConcurrency) + self.allowedExternalURLSchemes = allowedExternalURLSchemes + self.requiresExternalLinkConfirmation = requiresExternalLinkConfirmation + self.allowsInspectableWebViews = allowsInspectableWebViews + self.enablesVerboseWebViewLogging = enablesVerboseWebViewLogging } /// 默认配置实例,使用所有参数的默认值 diff --git a/Sources/RDReaderView/EPUBUI/Settings/RDEPUBReaderSettingsViewController.swift b/Sources/RDReaderView/EPUBUI/Settings/RDEPUBReaderSettingsViewController.swift index 95c6b38..02ecf24 100644 --- a/Sources/RDReaderView/EPUBUI/Settings/RDEPUBReaderSettingsViewController.swift +++ b/Sources/RDReaderView/EPUBUI/Settings/RDEPUBReaderSettingsViewController.swift @@ -239,6 +239,7 @@ final class RDEPUBReaderSettingsViewController: UIViewController { let selectedPreset = ThemePreset.allCases.first(where: { $0.theme == currentConfiguration.theme }) ?? .light updateThemeSelection(selectedPreset) + updateControlAccessibilityValues() } private func applyTheme(_ theme: RDEPUBReaderTheme) { @@ -279,9 +280,17 @@ final class RDEPUBReaderSettingsViewController: UIViewController { let isSelected = button.tag == preset.rawValue button.layer.borderWidth = isSelected ? 2 : 1 button.layer.borderColor = isSelected ? currentConfiguration.theme.toolControlTextColor.cgColor : currentConfiguration.theme.toolControlBorderUnselectColor.cgColor + button.accessibilityValue = isSelected ? "selected" : "unselected" } } + private func updateControlAccessibilityValues() { + fontChoiceControl.accessibilityValue = fontChoiceControl.titleForSegment(at: fontChoiceControl.selectedSegmentIndex) + lineHeightControl.accessibilityValue = lineHeightControl.titleForSegment(at: lineHeightControl.selectedSegmentIndex) + columnCountControl.accessibilityValue = columnCountControl.titleForSegment(at: columnCountControl.selectedSegmentIndex) + displayTypeControl.accessibilityValue = displayTypeControl.titleForSegment(at: displayTypeControl.selectedSegmentIndex) + } + @objc private func doneAction() { dismiss(animated: true) } @@ -312,6 +321,7 @@ final class RDEPUBReaderSettingsViewController: UIViewController { let choice = choices[index] guard choice != currentConfiguration.fontChoice else { return } currentConfiguration.fontChoice = choice + updateControlAccessibilityValues() onFontChoiceChange?(choice) } @@ -319,12 +329,14 @@ final class RDEPUBReaderSettingsViewController: UIViewController { let index = max(0, min(control.selectedSegmentIndex, lineHeightValues.count - 1)) let value = lineHeightValues[index] currentConfiguration.lineHeightMultiple = value + updateControlAccessibilityValues() onLineHeightChange?(value) } @objc private func columnCountChanged(_ control: UISegmentedControl) { let numberOfColumns = control.selectedSegmentIndex == 1 ? 2 : 1 currentConfiguration.numberOfColumns = numberOfColumns + updateControlAccessibilityValues() onColumnCountChange?(numberOfColumns) } @@ -339,6 +351,7 @@ final class RDEPUBReaderSettingsViewController: UIViewController { displayType = .pageCurl } currentConfiguration.displayType = displayType + updateControlAccessibilityValues() onDisplayTypeChange?(displayType) } diff --git a/Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextAnnotationOverlay.swift b/Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextAnnotationOverlay.swift index a8cbf5b..1ca3a29 100644 --- a/Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextAnnotationOverlay.swift +++ b/Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextAnnotationOverlay.swift @@ -2,6 +2,9 @@ import UIKit /// 原生文本渲染路径的批注覆盖层,负责绘制用户高亮、搜索命中高亮和当前选区装饰。 final class RDEPUBTextAnnotationOverlay: RDEPUBSelectionOverlayView { + private let normalSearchColor = UIColor(red: 0.21, green: 0.48, blue: 0.95, alpha: 0.16) + private let activeSearchColor = UIColor(red: 0.14, green: 0.42, blue: 0.95, alpha: 0.34) + /// 将用户高亮批注以富文本属性形式应用到页面内容上 /// - Parameters: /// - highlights: 高亮批注数组 @@ -58,8 +61,6 @@ final class RDEPUBTextAnnotationOverlay: RDEPUBSelectionOverlayView { ) { guard let searchState else { return } - let normalColor = UIColor(red: 248 / 255, green: 225 / 255, blue: 108 / 255, alpha: 0.55) - let activeColor = UIColor(red: 255 / 255, green: 159 / 255, blue: 67 / 255, alpha: 0.75) let pageRange = absoluteOffsetRange(for: page) let pageStart = pageRange.lowerBound let pageEndExclusive = pageRange.upperBound @@ -72,7 +73,7 @@ final class RDEPUBTextAnnotationOverlay: RDEPUBSelectionOverlayView { guard overlapStart < overlapEnd else { continue } let relativeRange = NSRange(location: Int(overlapStart - contentBaseOffset), length: Int(overlapEnd - overlapStart)) - let color = match == searchState.currentMatch ? activeColor : normalColor + let color = match == searchState.currentMatch ? activeSearchColor : normalSearchColor content.addAttribute(.backgroundColor, value: color, range: relativeRange) } } @@ -97,9 +98,6 @@ final class RDEPUBTextAnnotationOverlay: RDEPUBSelectionOverlayView { let pageEndExclusive = pageRange.upperBound if let searchState { - let normalColor = UIColor(red: 248 / 255, green: 225 / 255, blue: 108 / 255, alpha: 0.55) - let activeColor = UIColor(red: 255 / 255, green: 159 / 255, blue: 67 / 255, alpha: 0.75) - for match in searchState.matches { guard let matchStart = match.rangeLocation else { continue } let matchEnd = matchStart + match.rangeLength @@ -113,7 +111,7 @@ final class RDEPUBTextAnnotationOverlay: RDEPUBSelectionOverlayView { let isActive = match == searchState.currentMatch let kind: RDEPUBTextOverlayDecoration.Kind = isActive ? .activeSearch : .search - let color = isActive ? activeColor : normalColor + let color = isActive ? activeSearchColor : normalSearchColor background.append(RDEPUBTextOverlayDecoration(kind: kind, absoluteRange: absoluteRange, rects: rects, color: color)) } } diff --git a/Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextContentView.swift b/Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextContentView.swift index 35b24dc..b1b393e 100644 --- a/Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextContentView.swift +++ b/Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextContentView.swift @@ -14,6 +14,11 @@ protocol RDEPUBTextContentViewDelegate: AnyObject { didRequestSelectionAction action: RDEPUBAnnotationMenuAction, selection: RDEPUBSelection? ) + func textContentView( + _ contentView: RDEPUBTextContentView, + didActivateAttachmentText text: String, + sourceRect: CGRect + ) func textContentView( _ contentView: RDEPUBTextContentView, didRequestHighlightActions highlight: RDEPUBHighlight, @@ -33,6 +38,7 @@ final class RDEPUBTextContentView: UIView, UIGestureRecognizerDelegate { private var currentSelection: RDEPUBSelection? private var menuSelection: RDEPUBSelection? private var currentHighlights: [RDEPUBHighlight] = [] + private var currentSearchState: RDEPUBSearchState? weak var delegate: RDEPUBTextContentViewDelegate? #if canImport(DTCoreText) @@ -188,6 +194,7 @@ final class RDEPUBTextContentView: UIView, UIGestureRecognizerDelegate { currentSelection = nil menuSelection = nil currentHighlights = highlights + currentSearchState = searchState selectionController.clearSelection(renderView: coreTextRenderView) contentInsets = configuration.reflowableContentInsets backgroundColor = configuration.theme.contentBackgroundColor @@ -244,14 +251,6 @@ final class RDEPUBTextContentView: UIView, UIGestureRecognizerDelegate { overlayView.configure(page: page, selectionColor: overlayView.selectionColor, snapshot: interactionController.snapshot) #if canImport(DTCoreText) backgroundOverlayView.configure(page: page, selectionColor: overlayView.selectionColor, snapshot: interactionController.snapshot) - let (bgDecorations, fgDecorations) = overlayView.buildDecorations( - page: page, - highlights: [], - searchState: searchState, - interactionController: interactionController - ) - backgroundOverlayView.applyDecorations(bgDecorations) - overlayView.applyDecorations(fgDecorations) #endif updateAccessibilityDecorationSummary() @@ -262,6 +261,7 @@ final class RDEPUBTextContentView: UIView, UIGestureRecognizerDelegate { func clearSelection() { currentSelection = nil menuSelection = nil + currentSearchState = nil panGestureRecognizer.isEnabled = false selectionController.clearSelection(renderView: coreTextRenderView) overlayView.clearSelection() @@ -487,6 +487,17 @@ final class RDEPUBTextContentView: UIView, UIGestureRecognizerDelegate { interactionController.configure(layoutFrame: layoutFrame, page: page) overlayView.updateSnapshot(interactionController.snapshot) backgroundOverlayView.updateSnapshot(interactionController.snapshot) + + if let page = currentPage { + let (bgDecorations, fgDecorations) = overlayView.buildDecorations( + page: page, + highlights: [], + searchState: currentSearchState, + interactionController: interactionController + ) + backgroundOverlayView.applyDecorations(bgDecorations) + overlayView.applyDecorations(fgDecorations) + } } #endif @@ -539,6 +550,11 @@ final class RDEPUBTextContentView: UIView, UIGestureRecognizerDelegate { clearSelection() return } + if let attachmentText = attachmentText(at: point), + let sourceRect = attachmentSourceRect(at: point, fallbackPoint: point) { + delegate?.textContentView(self, didActivateAttachmentText: attachmentText, sourceRect: sourceRect) + return + } guard let highlight = highlight(at: point), let sourceRect = highlightSourceRect(for: highlight, fallbackPoint: point) else { return @@ -621,6 +637,41 @@ final class RDEPUBTextContentView: UIView, UIGestureRecognizerDelegate { return overlayView.convert(fallbackRect, to: self) } + private func attachmentText(at point: CGPoint) -> String? { + guard let page = currentPage else { return nil } + guard let attachmentRange = interactionController.snapshot?.attachment(at: point)?.stringRange else { + return nil + } + guard page.chapterContent.length > attachmentRange.location else { + return nil + } + let attachment = page.chapterContent.attribute(.attachment, at: attachmentRange.location, effectiveRange: nil) + +#if canImport(DTCoreText) + if let textAttachment = attachment as? DTTextAttachment, + let altText = (textAttachment.attributes["alt"] as? String)? + .trimmingCharacters(in: .whitespacesAndNewlines), + !altText.isEmpty { + return altText + } +#endif + + if let fileAttachment = attachment as? NSTextAttachment, + let altText = fileAttachment.accessibilityLabel?.trimmingCharacters(in: .whitespacesAndNewlines), + !altText.isEmpty { + return altText + } + return nil + } + + private func attachmentSourceRect(at point: CGPoint, fallbackPoint: CGPoint) -> CGRect? { + if let attachmentRect = interactionController.snapshot?.attachment(at: point)?.frame { + return overlayView.convert(attachmentRect, to: self) + } + let fallbackRect = CGRect(origin: fallbackPoint, size: CGSize(width: 1, height: 1)) + return overlayView.convert(fallbackRect, to: self) + } + override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if gestureRecognizer === panGestureRecognizer { return selectionController.isSelecting @@ -633,7 +684,7 @@ final class RDEPUBTextContentView: UIView, UIGestureRecognizerDelegate { return true } let point = tapGestureRecognizer.location(in: overlayView) - return highlight(at: point) != nil + return attachmentText(at: point) != nil || highlight(at: point) != nil } return true } diff --git a/Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextSelectionController.swift b/Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextSelectionController.swift index c0fc43e..e438216 100644 --- a/Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextSelectionController.swift +++ b/Sources/RDReaderView/EPUBUI/TextPage/RDEPUBTextSelectionController.swift @@ -140,12 +140,25 @@ final class RDEPUBTextSelectionController: NSObject { let totalLength = max(page.chapterContent.length - 1, 1) let globalStart = absoluteRange.location let globalEnd = absoluteRange.location + absoluteRange.length + let startAnchor = RDEPUBTextAnchor( + fileIndex: page.spineIndex, + row: 0, + column: 0, + chapterOffset: globalStart + ) + let endAnchor = RDEPUBTextAnchor( + fileIndex: page.spineIndex, + row: 0, + column: 0, + chapterOffset: globalEnd + ) return RDEPUBSelection( location: RDEPUBLocation( href: page.href, progression: Double(globalStart) / Double(totalLength), lastProgression: Double(max(globalEnd - 1, globalStart)) / Double(totalLength), - fragment: nil + fragment: nil, + rangeAnchor: RDEPUBTextRangeAnchor(start: startAnchor, end: endAnchor) ), text: selectedText, rangeInfo: RDEPUBTextOffsetRangeInfo(href: page.href, start: globalStart, end: globalEnd).jsonString() diff --git a/scripts/run_ui_regression.sh b/scripts/run_ui_regression.sh index 2f268ec..6ead072 100755 --- a/scripts/run_ui_regression.sh +++ b/scripts/run_ui_regression.sh @@ -27,18 +27,24 @@ SUMMARY_SCRIPT="$ROOT_DIR/scripts/summarize_ui_results.py" TARGET_NAME="ReadViewDemoUITests" QUICK_TESTS=( + ArchivePathValidationTests + BookmarkChromeStateTests BookmarkManagementTests BookmarkTests + CacheHardeningTests DisplayTypeTests ErrorAndEdgeCaseTests + ExternalLinkPolicyTests HighlightsManagementTests LocationPersistenceTests + NavigationBackwardTests PageNavigationTests ReaderAnnotationExtendedTests ReaderAnnotationTests ReaderOpenCloseTests ReaderToolbarTests SearchTests + SelectionAnnotateTests SelectionMenuTests SettingsEffectTests SettingsExtendedTests