# Phase 3: 重构属性体系与复杂分页器 - Research **Researched:** 2026-05-22 **Domain:** iOS EPUB native pagination / page metadata / CoreText layout semantics **Confidence:** HIGH ## User Constraints No `CONTEXT.md` exists for this phase. Planning is based on ROADMAP requirements, current code, Phase 1 strategy, and Phase 2 implementation artifacts only. ## Architectural Responsibility Map | Capability | Primary Tier | Secondary Tier | Rationale | |------------|-------------|----------------|-----------| | Page-level attributed string metadata | EPUBTextRendering | EPUBCore | Metadata must stay adjacent to chapter/page models so offsets, fragments, and attachments remain queryable. | | Stronger pagination semantics | EPUBTextRendering | CoreText | `ss_pageRanges(size:)` is the only current paginator; Phase 3 must replace or wrap it with richer layout decisions. | | Offset continuity for location/highlight/search | EPUBTextRendering | EPUBUI | `RDEPUBTextBook`, `RDEPUBTextContentView`, and `RDEPUBReadingSession` all assume stable chapter offsets. | | Reader state + navigation orchestration | EPUBCore | EPUBUI | `RDEPUBReadingSession` should keep owning navigation/page lookup; it should consume richer page metadata rather than invent a new UI model. | | Fixed / interactive EPUB rendering | EPUBCore | WebKit | Still out of scope; WebView paths remain unchanged. | ## Summary Phase 2 solved the renderer input problem. The native reflowable path now has chapter-scoped preprocessing, explicit CSS precedence, and resolver-backed resource diagnostics. What remains weak is the page model and paginator. `RDEPUBTextPaginationSupport.ss_pageRanges(size:)` still slices the chapter with `CTFrameGetVisibleStringRange` and returns only bare `NSRange` values. `RDEPUBTextPage` then stores those ranges plus `pageStartOffset` / `pageEndOffset`, but no block metadata, no attachment semantics, no page-edge reasons, and no way to explain why a page boundary landed where it did. That limitation is now the main blocker for `REND-03` and `REND-04`. Search, selection, highlights, `RDEPUBReadingSession.pageIndex(for:)`, and `RDEPUBTextContentView` all depend on stable offsets. Phase 3 therefore cannot replace `RDEPUBTextBook` with a new UI-only model. It has to extend the existing `RDEPUBTextChapter` / `RDEPUBTextPage` contract with richer metadata while preserving chapter text continuity and global offsets. The safest implementation path is incremental: 1. Define page metadata and attribute keys first. 2. Introduce an internal layouter / layout-frame abstraction that can reason about blocks, attachments, and page edges while still returning offset-stable page slices. 3. Validate the new paginator on real EPUBs with large blocks and images before touching Phase 4 reader reintegration. The repo already exposes the exact compatibility surface we need to preserve: - `RDEPUBTextContentView` derives relative highlight/search ranges from `pageStartOffset` and `pageEndOffset`. - `RDEPUBTextOffsetRangeInfo` serializes highlight ranges as absolute chapter offsets. - `RDEPUBTextSearchEngine` indexes chapter-level attributed strings and stores match ranges by chapter offset. - `RDEPUBReadingSession` still resolves navigation by href + progression and maps that back onto active pages. **Primary recommendation:** Build a richer internal pagination layer under `EPUBTextRendering` that emits extended `RDEPUBTextPage` metadata while keeping `pageStartOffset`, `pageEndOffset`, `fragmentOffsets`, and chapter text continuity intact. Do not move layout logic into `RDEPUBReaderController` or `RDEPUBTextContentView`. ## Standard Stack ### Core | Library | Version | Purpose | Why Standard | |---------|---------|---------|--------------| | Foundation | System | Metadata models, ranges, serialization, diagnostics | Existing page/highlight/search models are already Foundation-driven. | | CoreText | System | Low-level line/frame measurement | Needed to upgrade beyond pure visible-range slicing. | | UIKit | iOS 15+ SDK | Text page presentation and selection mapping | `RDEPUBTextContentView` still presents `NSAttributedString` pages and maps ranges back to offsets. | | DTCoreText | 1.6.28 | HTML-to-attributed-string import | Phase 3 builds on imported attributed content rather than replacing the typesetter. | ### Supporting | Library | Version | Purpose | When to Use | |---------|---------|---------|-------------| | ZIPFoundation | 0.9.20 | EPUB archive access | Real-book validation still depends on sample EPUB extraction. | | DTFoundation | 1.7.19 | DTCoreText support | Attachment and HTML import remain rooted here. | ### Alternatives Considered | Instead of | Could Use | Tradeoff | |------------|-----------|----------| | Extend `RDEPUBTextPage` / `RDEPUBTextChapter` | Create a brand-new page tree model | Would break current reader/highlight/search callers too early and enlarge Phase 4 scope. | | Introduce layouter/frame types under `EPUBTextRendering` | Keep growing `ss_pageRanges(size:)` as a single function | Hard to express page-edge reasons, block avoidance rules, and attachment metadata in one range-only helper. | | Preserve chapter offsets as the compatibility key | Shift to page-local only locations | Would break selection/highlight persistence and make Phase 4 much harder. | ## Architecture Patterns ### Pattern 1: Extend, don't replace, the book contract **What:** Add metadata to `RDEPUBTextPage` / `RDEPUBTextChapter` while preserving existing offset fields. **When to use:** When downstream readers already depend on `href + offsets + pages`. **Why:** Highlights, search matches, and navigation already serialize offsets and chapter hrefs. ### Pattern 2: Separate layouter from page serialization **What:** Use internal layouter/layout-frame types to compute page boundaries, then convert those results into `RDEPUBTextPage`. **When to use:** When pagination logic is becoming more complex than plain visible-range slicing. **Why:** Keeps `RDEPUBTextBookBuilder` as the chapter/page assembly boundary without forcing it to own all layout heuristics inline. ### Pattern 3: Preserve chapter-level continuity as the invariant **What:** Every new metadata structure should be traceable back to absolute chapter offsets. **When to use:** For block edges, attachments, fragment anchors, and page break reasons. **Why:** Current location/highlight/search behavior uses absolute chapter ranges; Phase 3 should enrich that graph, not replace it. ### Anti-Patterns to Avoid - Rewriting `RDReaderView` or page presentation controls in the name of pagination. - Introducing metadata that cannot be mapped back to `pageStartOffset` / `pageEndOffset`. - Letting validation rely only on “looks okay” manual checks without capturing page-edge or attachment evidence. ## Don't Hand-Roll | Problem | Don't Build | Use Instead | Why | |---------|-------------|-------------|-----| | Reader reintegration | A new reader shell | Existing `RDEPUBTextBook` / `RDEPUBReadingSession` contract | Phase 4 needs continuity, not a second UI model. | | Location persistence | Page-local ephemeral IDs only | Absolute chapter offsets + fragment compatibility | Current highlights and search results already use offset-based range info. | | Validation | Purely visual checks with no metadata evidence | Source assertions plus sample EPUB diagnostics | Complex pagination bugs need explainable metadata, not only screenshots. | ## Common Pitfalls ### Pitfall 1: Richer metadata that doesn't survive page serialization If metadata lives only in temporary layout structures and never lands on `RDEPUBTextPage`, Phase 4 cannot consume it. ### Pitfall 2: Better page edges but broken offset continuity If page boundaries improve but `pageStartOffset` / `pageEndOffset` drift from chapter content, highlights and search navigation regress immediately. ### Pitfall 3: Attachment-aware pagination without validation hooks Image/attachment rules are hard to trust unless the phase leaves behind enough metadata or demo diagnostics to confirm what happened on a given page boundary. ## State of the Art (2024-2025) | Old Approach | Current Approach | Impact | |--------------|------------------|--------| | `CTFrameGetVisibleStringRange` only | Layout pipeline with page-edge semantics and attachment-aware metadata | Needed to express avoid-break rules and richer page reasoning. | | Range-only page model | Page model with semantic metadata and diagnostics | Needed for later reader reintegration and regression debugging. | | Paginator hidden inside a helper | Layouter/frame responsibilities separated from book assembly | Improves testability and makes pagination regressions explainable. | ## Open Questions - Which page metadata belongs directly on `RDEPUBTextPage` versus a nested layout-metadata payload? - Should attachment/block metadata be encoded as custom `NSAttributedString.Key` values, separate page structs, or both? - How much of the new layouter/frame API should remain internal to `EPUBTextRendering` versus surfaced through `RDEPUBTextBookBuilder`? - Which sample EPUBs in `ReadViewDemo/ReadViewDemo/book/` best expose large-block and image boundary cases for repeatable validation?