Skip to content

Add support for EPUBs mixing reflowable and fixed-layout resources - #895

Merged
mickael-menu merged 3 commits into
developfrom
feature/mixed-layouts
Sep 14, 2026
Merged

mickael-menu merged 3 commits into
developfrom
feature/mixed-layouts

Conversation

@mickael-menu

Copy link
Copy Markdown
Member

Added

Navigator

  • The EPUB navigator supports publications mixing reflowable and fixed-layout resources, rendering each resource according to its own layout.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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

  • EPUBLayout is declared only as String, Sendable, so it does not satisfy the Hashable constraint required by Set. This makes the new EPUBLayouts type fail to compile; either add Hashable to EPUBLayout or 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 those Link values, 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

  • offsetFirstPage is 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 scroll effective for any publication containing a reflowable resource, but settings.scroll and presentation are still navigator-wide while fixed spreads ignore scrolling. On a mixed publication, a fixed spread can therefore report vertical scrolling and, when disablePageTurnsWhileScrolling is 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' isEffective values.
                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.

Comment thread TestApp/Sources/Reader/Common/Preferences/UserPreferences.swift
@mickael-menu
mickael-menu merged commit e2cd138 into develop Sep 14, 2026
5 checks passed
@mickael-menu
mickael-menu deleted the feature/mixed-layouts branch September 14, 2026 10:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants