145 lines
9.4 KiB
Markdown
145 lines
9.4 KiB
Markdown
# 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:
|
|
|
|
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`.
|
|
</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 `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.
|
|
|
|
</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 `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?
|
|
|
|
</open_questions>
|