9.4 KiB
Phase 3: 重构属性体系与复杂分页器 - Research
Researched: 2026-05-22 Domain: iOS EPUB native pagination / page metadata / CoreText layout semantics Confidence: HIGH
<user_constraints>
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.
</user_constraints>
<architectural_responsibility_map>
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. |
</architectural_responsibility_map>
<research_summary>
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:
- Define page metadata and attribute keys first.
- Introduce an internal layouter / layout-frame abstraction that can reason about blocks, attachments, and page edges while still returning offset-stable page slices.
- 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:
RDEPUBTextContentViewderives relative highlight/search ranges frompageStartOffsetandpageEndOffset.RDEPUBTextOffsetRangeInfoserializes highlight ranges as absolute chapter offsets.RDEPUBTextSearchEngineindexes chapter-level attributed strings and stores match ranges by chapter offset.RDEPUBReadingSessionstill 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.
</research_summary>
<standard_stack>
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. |
</standard_stack>
<architecture_patterns>
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
RDReaderViewor 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.
</architecture_patterns>
<dont_hand_roll>
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. |
</dont_hand_roll>
<common_pitfalls>
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.
</common_pitfalls>
<sota_updates>
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. |
</sota_updates>
<open_questions>
Open Questions
- Which page metadata belongs directly on
RDEPUBTextPageversus a nested layout-metadata payload? - Should attachment/block metadata be encoded as custom
NSAttributedString.Keyvalues, separate page structs, or both? - How much of the new layouter/frame API should remain internal to
EPUBTextRenderingversus surfaced throughRDEPUBTextBookBuilder? - Which sample EPUBs in
ReadViewDemo/ReadViewDemo/book/best expose large-block and image boundary cases for repeatable validation?
</open_questions>