Skip to content

Add support for choosing the rendered variant of EPUB resources - #898

Merged
mickael-menu merged 8 commits into
developfrom
feature/preferredResourceVariant
Sep 15, 2026
Merged

mickael-menu merged 8 commits into
developfrom
feature/preferredResourceVariant

Conversation

@mickael-menu

@mickael-menu mickael-menu commented Sep 14, 2026

Copy link
Copy Markdown
Member

Added

Navigator

  • New EPUBNavigatorViewController.Configuration.preferredResourceVariant to choose which variant of each resource is rendered among its alternates, such as the XHTML page or its bitmap fallback.
    • Bitmap resources in the reading order are rendered as fixed-layout, even in a reflowable publication.
  • New EPUBNavigatorDelegate.navigator(_:contentInsetFor:) to customize the content insets of a spread according to its EPUBLayout, for example to add margins only around the reflowable resources of a mixed-layout publication. It takes precedence over navigatorContentInset(_:).

Changed

Navigator

  • In the EPUB navigator, a fixed-layout resource displayed on its own when spreads are enabled is now centered, unless the publication provides an explicit page position (e.g. page-spread-left). It was previously displayed on the left or right half of the viewport, depending on the reading progression.

Streamer

  • EPUB spine items with a bitmap fallback are no longer swapped with their fallback. The spine item stays in the reading order and the bitmap is available in its alternates.
    • This changes the resources rendered by default and the href of the reported locations. Locations saved with the previous version are still restored by the EPUB navigator.
    • Use EPUBNavigatorViewController.Configuration.preferredResourceVariant = .image in the EPUB navigator configuration to render the bitmaps like before.

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

Four moderate issues remain involving position data, anchor navigation, and Divina profile detection.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds selectable EPUB resource variants, allowing HTML or bitmap fallbacks to be rendered while preserving authored spine order.

Changes:

  • Adds .default, .html, and .image variant preferences.
  • Updates fallback parsing, locator handling, spread behavior, and bitmap layout.
  • Adds tests, fixtures, and changelog documentation.
File summaries
File Summary
Tests/StreamerTests/Parser/EPUB/OPFParserTests.swift Tests fallback parsing and Divina inference.
Tests/StreamerTests/Fixtures/OPF/fallback-html-image-partial.opf Adds a partial fallback fixture.
Tests/NavigatorTests/EPUB/EPUBViewportAndLocationCalculatorTests.swift Tests rendered locator metadata.
Tests/NavigatorTests/EPUB/EPUBSpreadTests.swift Tests bitmap spread behavior.
Tests/NavigatorTests/EPUB/EPUBReadingOrderTests.swift Tests variant selection and locator resolution.
Sources/Streamer/Parser/EPUB/OPFParser.swift Preserves spine links and records fallbacks. Moderate (3 votes): Divina profile detection does not recognize bitmap alternates.
Sources/Navigator/EPUB/EPUBViewportAndLocationCalculator.swift Reports rendered resource hrefs and media types.
Sources/Navigator/EPUB/EPUBSpreadView.swift Resolves selected resources by variant.
Sources/Navigator/EPUB/EPUBResourceVariant.swift Defines supported resource variants.
Sources/Navigator/EPUB/EPUBReflowableSpreadView.swift Supports variant-aware locator navigation.
Sources/Navigator/EPUB/EPUBReadingOrder.swift Selects rendered variants. Moderate (1 vote): Positions are not normalized for fixed bitmap variants. Moderate (1 vote): Query/fragment handling can prevent anchor navigation.
Sources/Navigator/EPUB/EPUBNavigatorViewModel.swift Applies variant configuration. Moderate (1 vote): Position data can remain inconsistent with selected bitmap variants.
Sources/Navigator/EPUB/EPUBNavigatorViewController.swift Integrates variant-aware navigation.
CHANGELOG.md Documents the new behavior.
Review details

Suppressed comments (3)

Sources/Navigator/EPUB/EPUBNavigatorViewModel.swift:123

  • EPUBReadingOrder changes the media type and layout seen by the navigator, but positionsByReadingOrder is still loaded from the publication's original spine links. For an XHTML spine item with a bitmap fallback, the positions service creates multiple reflowable positions for XHTML; selecting .image then renders one fixed page while positionCount and locator calculation still use those positions, producing incorrect position and totalProgression values. Rebuild or disable positions for the rendered order when the preferred variant changes, and cover it with a location test.
        self.readingOrder = EPUBReadingOrder(
            readingOrder: readingOrder,
            preferredVariant: config.preferredResourceVariant

Sources/Navigator/EPUB/EPUBReadingOrder.swift:86

  • Setting the rendered copy to .fixed does not update the position list. positionsByReadingOrder is produced by EPUBPositionsService from publication.manifest.readingOrder, where these HTML spine links are still reflowable, so a bitmap fallback can retain several HTML positions. The fixed spread then reports/preloads those positions and calculates total progression as if the bitmap were reflowable; normalize the positions for the selected rendered variants (one position for each bitmap) before using them.
    if link.mediaType?.isBitmap == true {
        link.properties.epubLayout = .fixed

Sources/Navigator/EPUB/EPUBReadingOrder.swift:63

  • index(of:) intentionally matches a rendered link after stripping query/fragment, but this guard compares the URLs including those components. A locator for the rendered resource such as p1.xhtml#id is therefore treated as an alternate, its fragment is cleared below, and anchor navigation fails. Compare query/fragment-stripped URLs (or track whether the match was the rendered link) before clearing locations.
        guard !href.isEquivalentTo(locator.href) else {
            return (index, locator)
        }
  • Files reviewed: 14/14 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 Sources/Streamer/Parser/EPUB/OPFParser.swift
@mickael-menu
mickael-menu merged commit 93ea4b3 into develop Sep 15, 2026
5 checks passed
@mickael-menu
mickael-menu deleted the feature/preferredResourceVariant branch September 15, 2026 15:33
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