Add support for EPUBs mixing reflowable and fixed-layout resources - #895
Merged
Merged
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved layout-resolution and mixed-layout preference issues remain, along with a compile-time Hashable issue.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds Navigator support for EPUB publications mixing reflowable and fixed-layout resources.
Changes:
- Parses and exposes per-resource layout overrides.
- Resolves positioning, spreads, views, CSS, and preferences by resource layout.
- Expands tests and updates preferences documentation and changelog.
File summaries
| File | Summary |
|---|---|
Tests/StreamerTests/Parser/EPUB/Services/EPUBPositionsServiceTests.swift |
Adds mixed-layout position tests. |
Tests/StreamerTests/Parser/EPUB/OPFParserTests.swift |
Tests parsing of layout overrides. |
Tests/StreamerTests/Fixtures/OPF/links-properties.opf |
Adds layout and page-spread fixture properties. |
Tests/SharedTests/Publication/ManifestTests.swift |
Updates manifest assertions. |
Tests/SharedTests/Publication/Extensions/EPUB/Properties+EPUBTests.swift |
Tests link layout properties. |
Tests/SharedTests/Publication/Extensions/EPUB/Metadata+EPUBTests.swift |
Tests effective layout resolution. |
Tests/NavigatorTests/EPUB/EPUBSpreadTests.swift |
Adds mixed-layout spread coverage. |
TestApp/Sources/Reader/Common/Preferences/UserPreferences.swift |
Updates layout-aware preference controls. |
Sources/Streamer/Parser/EPUB/Services/EPUBPositionsService.swift |
Calculates positions by resource layout. |
Sources/Streamer/Parser/EPUB/OPFParser.swift |
Parses per-resource layout overrides. |
Sources/Shared/Publication/Extensions/EPUB/Properties+EPUB.swift |
Exposes link layout properties. |
Sources/Shared/Publication/Extensions/EPUB/Metadata+EPUB.swift |
Resolves publication and resource layouts. |
Sources/Navigator/EPUB/Preferences/EPUBPreferencesEditor.swift |
Tracks layout-aware preference effectiveness. |
Sources/Navigator/EPUB/Preferences/EPUBPreferences.swift |
Updates EPUB preference definitions and documentation. |
Sources/Navigator/EPUB/EPUBSpread.swift |
Supports mixed-layout spread construction. |
Sources/Navigator/EPUB/EPUBNavigatorViewModel.swift |
Applies layout-specific CSS behavior. |
Sources/Navigator/EPUB/EPUBNavigatorViewController.swift |
Selects rendering views and insets per spread. |
Sources/Navigator/EPUB/EPUBLayouts.swift |
Tracks layouts in the reading order. |
Sources/Navigator/EPUB/EPUBExtensions.swift |
Removes the relocated metadata extension. |
docs/Guides/Navigator/Preferences.md |
Documents mixed-layout preferences. |
CHANGELOG.md |
Records the new mixed-layout capability. |
Review details
Suppressed comments (5)
Sources/Navigator/EPUB/EPUBLayouts.swift:19
EPUBLayoutis declared only asString, Sendable, so it does not satisfy theHashableconstraint required bySet. This makes the newEPUBLayoutstype fail to compile; either addHashabletoEPUBLayoutor store the raw values instead.
private let layouts: Set<EPUBLayout>
Sources/Navigator/EPUB/EPUBNavigatorViewModel.swift:317
- When the navigator is created with its supported custom
readingOrder, the spread and preference code resolves layout from thoseLinkvalues, but this guard resolves it from the publication's first matching link instead. A custom link with a per-resource layout override will therefore select the reflowable CSS path even when the spread is fixed (or vice versa). Use the matching custom-reading-order link for layout resolution while retaining the publication link for the media-type check.
let link = publication.linkWithHREF(href),
link.mediaType?.isHTML == true,
publication.metadata.epubLayout(of: link) == .reflowable
Sources/Navigator/EPUB/Preferences/EPUBPreferencesEditor.swift:286
offsetFirstPageis consumed only when the first reading-order resource is fixed (EPUBSpread.swift:151-164). In a mixed publication whose first resource is reflowable and a later resource is fixed,layouts.contains(.fixed)makes this preference report as effective even though changing it cannot affect any spread. Base this predicate on the first rendered resource, or change spread construction to apply the preference to the intended fixed resource.
isEffective: { [layouts] in
layouts.contains(.fixed)
&& $0.settings.spread != .never
Sources/Navigator/EPUB/Preferences/EPUBPreferencesEditor.swift:380
- This makes
scrolleffective for any publication containing a reflowable resource, butsettings.scrollandpresentationare still navigator-wide while fixed spreads ignore scrolling. On a mixed publication, a fixed spread can therefore report vertical scrolling and, whendisablePageTurnsWhileScrollingis enabled, lose the outer pager's horizontal page turns. The scroll/pagination behavior needs to be resolved per current spread rather than only by the publication's layout set.
isEffective: { [layouts] in
layouts.contains(.reflowable) && !$0.settings.verticalText
docs/Guides/Navigator/Preferences.md:126
- This sample still selects a single preference group from
defaultLayout, which is only the publication fallback. In a mixed publication it can hide all controls for the other effective layout, so the documented mixed-layout behavior is not represented; the example should render each group according to the individual preferences'isEffectivevalues.
switch editor.defaultLayout {
- Files reviewed: 21/21 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Added
Navigator