All notable changes to this project will be documented in this file. Take a look at the migration guide to upgrade between two major versions.
- New
PlaygroundiOS app – a minimal SwiftUI sample demonstrating how to use the Readium Swift Toolkit and to test its API.Recipes/contains self-contained and explained code you can reuse in your own application.App/folder contains the scaffolding (file management, navigation, error handling) needed to run the Playground.
- Added support for SVG covers in
ResourceCoverService. SVG images can now be used as publication covers and are rendered to bitmaps (contributed by @grighakobian).
- Carthage is no longer a supported distribution method. Please migrate to Swift Package Manager or CocoaPods.
- All public types that parsed or serialized JSON now use the new type-safe
JSONValueenum instead ofAny/[String: Any]. See the migration guide for upgrade instructions.
- The
DirectionalNavigationAdapter's policies and animated transitions are now mutable, allowing you to update the adapter's behavior after creation.
- Fixed
Publication.coverFitting(maxSize:)producing incorrectly scaled images with pixel offsets. - Fixed parsing of URI templates.
- Fixed
URITemplatenot recognizing{&...}(form-style query continuation) expressions. - Fixed
URITemplateexpanding a form-style expression ({?...}or{&...}) to a bare?or&when none of the listed variables are provided. It now correctly expands to an empty string.
- Fixed
- #737 Improved page turn animations in the EPUB navigator.
- Fixed screen glitches when turning with animations disabled.
- A slide animation is now used when navigating between adjacent resources.
- The EPUB navigator now reports a continuous
locator.locations.totalProgressionvalue, interpolated from the actual scroll position within the resource's global progression range. Previously, the value was quantized to the nearest position in the position list. - Fixed a race condition in
EPUBNavigatorViewControllerwhere rapidly callingapply(decorations:in:)for the same group could cause multiple decorations to appear simultaneously. - #721 Fixed position of EPUB decorations when using the paragraph indent preference.
- Fixed parsing of EPUB contributors.
- Fixed
media:narratorcontributors not being recognized as narrators. - Fixed
dc:creatorelements with a known MARC relator role (e.g.opf:role="trl") being incorrectly routed to theauthorcollection instead of the role's collection. - A known role on a contributor no longer leaks into the
rolesfield of theContributorobject when it is already expressed by the contributor's collection (e.g.authors,publishers).
- Fixed
- New Keychain-based implementations of the LCP license and passphrase repositories:
LCPKeychainLicenseRepositoryandLCPKeychainPassphraseRepository.- Stored securely in the iOS/macOS Keychain.
- Persist across app reinstalls.
- Optionally synchronized across devices via iCloud Keychain.
- The EPUB navigator no longer requires an HTTP server. Publication resources are now served directly to the web views using a custom URL scheme handler.
- The
httpServerparameter ofEPUBNavigatorViewControlleris deprecated and ignored.
- The
CBZNavigatorViewControlleris now deprecated.- Open CBZ publications with
EPUBNavigatorViewControllerinstead, which has more configuration options and preferences.
- Open CBZ publications with
ReadiumAdapterLCPSQLiteis now deprecated in favor of the built-in Keychain repositories. See the migration guide for instructions.
- Fixed casting of
ResourceProperties'smediaType(contributed by @lbeus).
- The first resource of a fixed-layout EPUB is now displayed on its own by default, matching Apple Books behavior.
- Fixed the default spread position for single fixed-layout EPUB spreads that are not the first page.
- Fixed the
printmethod consuming copy rights instead of print rights.
- Added support for JXL (JPEG XL) bitmap images. JXL is decoded natively on iOS 17+.
Publication.cover()now falls back on the first reading order resource if it's a bitmap image and no cover is declared.
- Support for displaying Divina (image-based publications like CBZ) in the fixed-layout EPUB navigator.
- Bitmap images in the EPUB reading order are now supported as a fixed layout resource.
- Added
offsetFirstPagepreference for fixed-layout EPUBs to control whether the first page is displayed alone or alongside the second page when spreads are enabled.
- The
ImageParsernow extracts metadata fromComicInfo.xmlfiles in CBZ archives. - EPUB manifest item fallbacks are now exposed as
alternatesin the correspondingLink. - EPUBs with only bitmap images in the spine are now treated as Divina publications with fixed layout.
- When an EPUB spine item is HTML with a bitmap image fallback (or vice versa), the image is preferred as the primary link.
- Standalone audio files (e.g. MP3) metadata extraction now includes
narrators(from the composer metadata fields) and merges artist metadata intoauthors, following conventions used by common audiobook tools.
- The iOS minimum deployment target is now iOS 15.0.
- Accessibility display strings are now sourced from the thorium-locales repository (instead of W3C's repository). Contributions are welcome on Weblate.
- The LCP dialog used by
LCPDialogAuthenticationhas been redesigned.- Breaking: The LCP dialog localization string keys have been renamed. If you overrode these strings in your app, you must update them. See the migration guide for the key mapping.
- LCP localized strings are now sourced from the thorium-locales repository. Contributions are welcome on Weblate.
- The EPUB manifest item
idattribute is no longer exposed inLink.properties. - Removed title inference based on folder names within image and audio archives. Use the archive's filename instead.
- PDF documents are now opened off the main thread, preventing UI freezes with large files.
- Fixed providing a custom reading order to the
EPUBNavigatorViewController(contributed by @lbeus).
- Added
DragPointerObserverto recognize drag gestures with pointer events. - Added
DirectionalNavigationAdapter.onNavigationcallback to be notified when a navigation action is triggered.- This callback is called before executing any navigation action.
- Useful for hiding UI elements when the user navigates, or implementing analytics.
- Added swipe gesture support for navigating in PDF paginated spread mode.
- Added
fitpreference for fixed-layout publications (PDF and FXL EPUB) to control how pages are scaled within the viewport.- In the PDF navigator, it is only effective in scroll mode. Paginated mode always uses
pagefit due to PDFKit limitations.
- In the PDF navigator, it is only effective in scroll mode. Paginated mode always uses
PDFNavigatorViewController.scalesDocumentToFitis now deprecated and non-functional. The navigator always scales the document to fit the viewport.
- Support for asynchronous callbacks with
onCreatePublication(contributed by @smoores-dev).
- The
Fitenum has been redesigned to fit the PDF implementation.- Breaking change: Update any code using the old
Fitenum values.
- Breaking change: Update any code using the old
- The fixed-layout navigators (PDF and FXL EPUB)'s content inset behavior has changed:
- iPhone: Continues to apply window safe area insets (to account for notch/Dynamic Island).
- iPad/macOS: Now displays edge-to-edge with no automatic safe area insets.
- You can customize this behavior with
VisualNavigatorDelegate.navigatorContentInset(_:).
- Fixed EPUB fixed-layout spread settings not updating after device rotation when the app was in the background.
- Fixed zoom-to-fit scaling in PDF paginated spread mode when
offsetFirstPageis enabled.
- Fixed crash when an EPUB resource is declared as LCP-encrypted in the manifest but contains unencrypted data.
- Added
VisualNavigatorDelegate.navigatorContentInset(_:)to customize the content and safe-area insets used by the navigator.- By default, the navigator uses the window's
safeAreaInsets, which can cause content to shift when the status bar is shown or hidden (since those insets change). To avoid this, implementnavigatorContentInset(_:)and return insets that remain stable across status bar visibility changes — for example, a top inset large enough to accommodate the maximum expected status bar height.
- By default, the navigator uses the window's
- Added
[TTSVoice].filterByLanguage(_:)to filter TTS voices by language and region. - Added
[TTSVoice].sorted()to sort TTS voices by region, quality, and gender. - New experimental positioning of EPUB decorations that places highlights behind text to improve legibility with opaque decorations (contributed by @ddfreiling).
- To opt-in, initialize the
EPUBNavigatorViewController.Configurationobject withdecorationTemplates: HTMLDecorationTemplate.defaultTemplates(alpha: 1.0, experimentalPositioning: true).
- To opt-in, initialize the
- Added an initializer parameter for providing a custom device identifier (contributed by @dewantawsif).
- You must ensure the identifier is unique and stable for the device (persist and reuse across app launches).
- Recommended: generate an app-scoped UUID and store it securely (e.g., in the Keychain); avoid hardware or advertising identifiers.
- You can use
LCPService.injectLicenseDocument(_:in)to insert an LCPL into a package, if you downloaded it manually instead of usingLCPService.acquirePublication().
EPUBNavigatorViewController.Configuration.contentInsetnow expects values that already include the safe area insets.- If you previously supplied content-only margins, update them to add the safe-area values to preserve the same visible layout.
- Alternatively, implement
VisualNavigatorDelegate.navigatorContentInset(_:)to compute and return the full insets (content + safe area), helping avoid layout shifts when system UI (e.g., the status bar) appears or disappears.
- Eloquence and novelty TTS voices are removed from the
PublicationSpeechSynthesizerAPI, as they are not a good fit to read publications.
- The LCP License Document is now accessible via
publication.lcpLicense?.license, even if the license validation fails with a status error or missing passphrase. This is useful for checking the end date of an expired license or renew a license.
- The safe area insets strategy was adjusted to take into account changes in iOS/iPadOS 26.
- Fixed the lost progression with the EPUB navigator when the application becomes active again after the system terminated the WebKit process.
- You can now access the
viewportproperty of anEPUBNavigatorViewControllerto obtain information about the visible portion of the publication, including the visible positions and reading order indices.
- The Presentation Hints properties are deprecated from the Readium Web Publication Manifest models. See the official documentation.
- EPUB series added with Calibre now take precedence over the native EPUB ones in the
belongsToSeriesRWPM property.
- #639 Optimized the opening of really large LCP protected publications.
- #70 Fixed restoring the reading progression with RTL EPUB.
- EPUB vertical text in scrolling mode:
- #556 Fixed reporting and restoring the reading progression.
- Added support for decorations (highlights).
- #635 Fixed overlapping FXL pages in landscape orientation.
- Implementation of the W3C Accessibility Metadata Display Guide specification to facilitate displaying accessibility metadata to users. See the dedicated user guide.
- Support for starting from a progression in the HTML content iterator.
- New link
relsin thereadingOrderand EPUBlandmarksto mark:cover: the title pagecontents: the table of contentsstart: the first actual chapter
- A new
InputObservingAPI has been added to enable more flexible gesture recognition and support for mouse pointers. See the dedicated user guide.
- The EPUB 2
<guide>element is now parsed into the RWPMlandmarkssubcollection when no EPUB 3landmarksnavigation document is declared.
- A brand new LCP authentication dialog for SwiftUI applications. See the accompanying user guide.
- Fixed several issues with the EPUB navigator cursor and pointer events.
- Fixed the cursor shape on iPadOS when using a physical trackpad or mouse.
- Fixed multiple tap events broadcasted while running on macOS.
- #449 Fixed misaligned EPUB navigator when it does not span the full screen width.
- Support for W3C's Text & data mining Reservation Protocol in our metadata models.
- Support for accessibility exemption metadata, which allows content creators to identify publications that do not meet conformance requirements but fall under exemptions in a given juridiction.
- Support for EPUB Accessibility 1.1 conformance profiles.
- Support for streaming an LCP-protected publication from its License Document (LCPL). Take a look at the LCP guide for more information.
- The
absoluteURLandrelativeURLextensions onURLConvertiblewere removed as they conflict with the nativeURL.absoluteURL.- If you were using them, you can for example still use
anyURL.absoluteURLinstead.
- If you were using them, you can for example still use
- go-toolkit#92 The accessibility feature
printPageNumbersis deprecated in favor ofpageNavigation.
- A
selflink is not required anymore when parsing a RWPM.
- Fixed going to a link containing a fragment in the PDF navigator, for example from the table of contents.
- Support for streaming ZIP packages over HTTP. This lets you open a remote EPUB, audiobook, or any other ZIP-based publication without needing to download it first.
- The
close()andCloseableAPIs are now deprecated. Resources are automatically released upondeinit, which aligns better with Swift.
- Fixed a regression that caused some LCP passphrases to no longer match the protected publication.
- Fixed race condition when calling
submitPreferences()before the EPUB navigator is fully initialized.
- The Readium Swift toolkit now requires a minimum of iOS 13.4.
- All the libraries are now available on a dedicated Readium CocoaPods Specs repository. Take a look at the migration guide to migrate.
- The
EPUBNavigatorViewController.Configuration.disablePageTurnsWhileScrollingproperty disables horizontal swipes for navigating to previous or next resources when scroll mode is enabled. When set totrue, you must implement your own mechanism to move to the next resource (contributed by @alecdhansen).
- The default
ZIPArchiveOpeneris now using ZIPFoundation instead of Minizip, with improved performances when reading ranges ofstoredZIP entries. - Improvements in the HTTP client:
- The
consumeclosure ofHTTPClient.stream()can now return an error to abort the HTTP request. HTTPErrorhas been refactored for improved type safety and a clearer separation of connection errors versus HTTP errors.DefaultHTTPClientno longer automatically restarts a failedHEADrequest as aGETto retrieve the response body. If you relied on this behavior, you can implement it using a customDefaultHTTPClientDelegate.httpClient(_:recoverRequest:fromError:).
- The
- Fixed a crash using
HTTPClient.download()when the device storage is full.
- #509 Removed the "Copy Link with Highlight" and "Writing Tools" EPUB editing actions on newer devices.
- Fixed a data race in the OPDS 1 parser.
TableOfContentsServicecan now be used to customize the computation ofpublication.tableOfContents().
- The table of contents of an LCP-protected PDF is now extracted directly from the PDF if the
tableOfContentsproperty inmanifest.jsonis empty.
- #489 Fix crash related to Fuzi when compiling with Xcode 16 in release mode.
- #502 Fixed accessibility editing actions on iOS 18.
- #459 Fixed the stack overflow issue that occurred when running the text-to-speech on an EPUB file with many empty resources.
- #490 Fixed issue loading fixed-layout EPUBs.
- Support for standalone audio files and their metadata (contributed by @domkm).
- The Readium Swift toolkit now requires a minimum of iOS 13.
- Plenty of completion-based APIs were changed to use
asyncfunctions instead.
- A new
Formattype was introduced to augmentMediaTypewith more precise information about the format specifications of anAsset. Fetcherwas replaced with a simplerContainertype.PublicationAssetwas replaced byAsset, which contains aFormatand access to the underlyingContainerorResource.- The
ResourceErrorhierarchy was revamped and simplified (seeReadError). Now it is your responsibility to provide a localized user message for each error case. - The
Linkproperty key for archive-based publication assets (e.g. an EPUB/ZIP) is nowhttps://readium.org/webpub-manifest/properties#archiveinstead ofarchive. - The API of
HTTPServerslightly changed to be more future-proof.
- The
Streamerobject was deprecated in favor of smaller segregated APIs:AssetRetrieverandPublicationOpener.
- EPUB: The
scrollpreference is now forced totruewhen rendering vertical text (e.g. CJK vertical). See this discussion for the rationale.
- The Readium LCP persistence layer was extracted to allow applications to provide their own implementations. Take a look at the migration guide for guidance.
- Optimized scrolling to an EPUB text-based locator if it contains a CSS selector.
- The first resource of a fixed-layout EPUB is now displayed on its own when spreads are enabled and the author has not set a
page-spread-*property. This is the default behavior in major reading apps like Apple Books. - #471 EPUB: Fixed reporting the current location when submitting new preferences.
- The
R2Shared,R2StreamerandR2Navigatorpackages are now calledReadiumShared,ReadiumStreamerandReadiumNavigator. - Many APIs now expect one of the new URL types (
RelativeURL,AbsoluteURL,HTTPURLandFileURL). This is helpful because:- It validates at compile time that we provide a URL that is supported.
- The API's capabilities are better documented, e.g. a download API could look like this :
download(url: HTTPURL) -> FileURL.
LinkandLocator'shrefare normalized as valid URLs to improve interoperability with the Readium Web toolkits.- You MUST migrate your database if you were persisting HREFs and Locators. Take a look at the migration guide for guidance.
- Links are not resolved to the
selfURL of a manifest anymore. However, you can still normalize the HREFs yourselves by callingManifest.normalizeHREFsToSelf(). Publication.localizedTitleis now optional, as we cannot guarantee a publication will always have a title.
- #489 Fix crash related to Fuzi when compiling with Xcode 16 in release mode.
- #502 Fixed accessibility editing actions on iOS 18.
- #483 Fixed build on Xcode 16.
- #444 Fixed resolving titles of search results when the table of contents items contain fragment identifiers.
- #428 Fixed crash with the
shareediting action on iOS 17. - #428 Fixed showing look up and translate editing actions on iOS 17.
- #417 Support for the new 2.x LCP Profiles.
- You can now use
DefaultHTTPClientDelegate.httpClient(_:request:didReceive:completion:)to handle authentication challenges (e.g. Basic) withDefaultHTTPClient.
- The
AudioNavigatorAPI has been promoted to stable and ships with a new Preferences API. - The new
NavigatorDelegate.didFailToLoadResourceAt(_:didFailToLoadResourceAt:withError:)delegate API notifies when an error occurs while loading a publication resource (contributed by @ettore).
- From iOS 13 to 15, PDF text selection is disabled on protected publications disabling the Copy editing action.
- The Share editing action is disabled for any protected publication.
- Fixed starting the TTS from the current EPUB position.
- #396 Ensure we stop the activity indicator when an EPUB resource fails to load correctly (contributed by @ettore).
- #399 Zipped Audio Books and standalone audio files are now recognized.
- You can now customize the playback refresh rate of
_AudiobookNavigatorin its configuration. - The EPUB navigator automatically moves to the next resource when VoiceOver reaches the end of the current one.
- You should not subclass
PDFNavigatorViewControlleranymore. If you need to overridesetupPDFView, you can do so by implementing thePDFNavigatorDelegateprotocol.
- Zipped Audio Book archives are now detected even if they contain bitmap entries.
- #344 EPUB: Fixed lost position when rotating quickly the screen several times.
- #350 Restore the ability to subclass the
PDFNavigatorViewController. - Fixed activating the scroll mode when VoiceOver is enabled in the EPUB navigator.
- Support for Xcode 15.
- The
PublicationSpeechSynthesizer(TTS) now supports background playback by default.- You will need to enable the Audio Background Mode in your app's build info.
- Support for non-linear EPUB resources with an opt-in in reading apps (contributed by @chrfalch in #332 and #331).
- Override loading non-linear resources with
VisualNavigatorDelegate.navigator(_:shouldNavigateToLink:). - Present a new
EPUBNavigatorViewControllerby providing a customreadingOrderwith only this resource to the constructor.
- Override loading non-linear resources with
- Improved performance when adding hundreds of HTML decorations at once.
- Fixed broadcasting the
PublicationSpeechSynthesizerwith AirPlay when the screen is locked.
AudioSessionandNowPlayingInfoare now stable!- You need to provide the configuration of the Audio Session to the constructor of
PublicationSpeechSynthesizer, instead ofAVTTSEngine.
- The Readium toolkit now requires iOS 11.0+.
- The
autospread setting is now available for fixed-layout EPUBs. It will display two pages in landscape and a single one in portrait.
- The EPUB content iterator now returns
audioandvideoelements and fill in theprogressionandtotalProgressionlocator properties.
EPUBNavigatorViewController.firstVisibleElementLocator()now returns the first block element that is visible on the screen, even if it starts on previous pages.- This is used to make sure the user will not miss any context when restoring a TTS session in the middle of a resource.
- Fixed the PDF
autospread setting and scaling pages when rotating the screen. - Fixed navigating to the first chapter of an audiobook with a single resource (contributed by @grighakobian).
- Prevent auto-playing videos in EPUB publications.
- Fixed various memory leaks and data races.
- The
WKWebViewis now inspectable again with Safari starting from iOS 16.4. - Fixed crash in the
PublicationSpeechSynthesizerwhen closing the navigator without stopping it first. - Fixed pausing the
PublicationSpeechSynthesizerright before starting the utterance. - Fixed the audio session kept opened while the app is in the background and paused.
- Fixed the Attribute dir redefined error when the EPUB resource already has a
dirattribute. - #309 Fixed restoring the EPUB location when the application was killed in the background (contributed by @triin-ko).
- Fix issue with the TTS starting from the beginning of the chapter instead of the current position.
- Positions computation, TTS and search is now enabled for Readium Web Publications conforming to the EPUB profile.
- New
VisualNavigatorDelegateAPIs to handle keyboard events (contributed by @lukeslu).- This can be used to turn pages with the arrow keys, for example.
- Support for custom fonts with the EPUB navigator.
- A brand new user preferences API for configuring the EPUB and PDF Navigators. This new API is easier and safer to use. To learn how to integrate it in your app, please refer to the user guide and migration guide.
- New EPUB user preferences:
fontWeight- Base text font weight.textNormalization- Normalize font style, weight and variants, which improves accessibility.imageFilter- Filter applied to images in dark theme (darken, invert colors)language- Language of the publication content.readingProgression- Direction of the reading progression across resources, e.g. RTL.typeScale- Scale applied to all element font sizes.paragraphIndent- Text indentation for paragraphs.paragraphSpacing- Vertical margins for paragraphs.hyphens- Enable hyphenation.ligatures- Enable ligatures in Arabic.
- New PDF user preferences:
backgroundColor- Background color behind the document pages.offsetFirstPage- Indicate if the first page should be displayed in its own spread.pageSpacing- Spacing between pages in points.readingProgression- Direction of the reading progression across resources, e.g. RTL.scrollAxis- Scrolling direction whenscrollis enabled.scroll- Indicate if pages should be handled using scrolling instead of pagination.spread- Enable dual-page mode.visibleScrollbar- Indicate whether the scrollbar should be visible while scrolling.
- New EPUB user preferences:
- The new
DirectionalNavigationAdaptercomponent helps you to turn pages with the arrows and space keyboard keys or taps on the edge of the screen.
PublicationServeris deprecated. See the the migration guide to migrate the HTTP server.
- The EPUB
UserSettingscomponent is deprecated and replaced by the new Preferences API. Take a look at the user guide and migration guide.
- The
defineediting action replaceslookupon iOS 16+. When enabled, it will show both the "Look Up" and "Search Web" menu items. - Prevent navigation in the EPUB while it is being loaded.
- Fixed a race condition issue with the
AVTTSEngine, when pausing utterances. - Fixed crash with
PublicationSpeechSynthesizer, when the currently played word cannot be resolved. - Fixed EPUB tap event sent twice when using a mouse (e.g. on Apple Silicon or with a mouse on an iPad).
- Support for the accessibility metadata in RWPM per Schema.org Accessibility Properties for Discoverability Vocabulary.
- Extract the raw content (text, images, etc.) of a publication.
- Parse EPUB accessibility metadata (see documentation).
Locator(link: Link)is deprecated as it may create an incorrectLocatorif the linktypeis missing.- Use
publication.locate(Link)instead.
- Use
- #244 Fixed build with Xcode 14 and Carthage/CocoaPods.
- Fixed memory leaks in the EPUB and PDF navigators.
- #61 Fixed serving EPUB resources when the HREF contains an anchor or query parameters.
- Performance issue with EPUB fixed-layout when spreads are enabled.
- Disable scrolling in EPUB fixed-layout resources, in case the viewport is incorrectly set.
- Fix vertically bouncing EPUB resources in iOS 16.
- Fixed memory leak in the
PublicationServer.
- The LCP authentication dialog is now fully localized and supports Dark Mode (contributed by @openm1nd).
- Get the sanitized
Locatortext ready for user display withlocator.text.sanitized(). - A new
Publication.conforms(to:)API to identify the profile of a publication. - Support for the
conformsToRWPM metadata, to identify the profile of aPublication. - Support for right-to-left PDF documents by extracting the reading progression from the
ViewerPreferences/Directionmetadata. - HTTP client:
- A new
HTTPClient.download()API to download HTTP resources to a temporary location. HTTPRequestandDefaultHTTPClienttake an optionaluserAgentproperty to customize the user agent.
- A new
- The new
NavigatorDelegate.navigator(_:didJumpTo:)API is called every time the navigator jumps to an explicit location, which might break the linear reading progression.- For example, it is called when clicking on internal links or programmatically calling
Navigator.go(to:), but not when turning pages. - You can use this callback to implement a navigation history by differentiating between continuous and discontinuous moves.
- For example, it is called when clicking on internal links or programmatically calling
Publication.formatis now deprecated in favor of the newPublication.conforms(to:)API which is more accurate.- For example, replace
publication.format == .epubwithpublication.conforms(to: .epub)before opening a publication with theEPUBNavigatorViewController.
- For example, replace
- The
LCPServicenow uses a providedHTTPClientinstance for all HTTP requests.
- #14 Backward compatibility (iOS 10+) of JavaScript files is now handled with Babel.
- Throttle the reload of EPUB spreads to avoid losing the position when the reader gets back to the foreground.
- Fixed the notification of acquisition progress.
- Support for Paragraph Margins user setting.
- A new
translateEPUB and PDF editing action is available for iOS 15.
- Improved performances of the search service used with EPUB.
- Fixed turning pages of an EPUB reflowable resource with an odd number of columns. A virtual blank trailing column is appended to the resource when displayed as two columns.
- Fix crash using the default
LCPDialogViewControllerwith CocoaPods.
- Support for Swift Package Manager (contributed by @stevenzeck).
- (alpha) A new Publication
SearchServiceto search through the resources' content with a default implementationStringSearchService. Linkobjects from archive-based publication assets (e.g. an EPUB/ZIP) have additional properties for entry metadata."properties" { "archive": { "entryLength": 8273, "isEntryCompressed": true } }
- New
UserProperties.removeProperty(forReference:)API to remove unwanted Readium CSS properties (contributed by @ettore).
- EPUB navigator:
- The EPUB navigator is now able to navigate to a
Locatorusing itstextcontext. This is useful for search results or highlights missing precise locations. - New
EPUBNavigatorViewController.evaluateJavaScript()API to run a JavaScript on the currently visible HTML resource. - New
userSettingsproperty forEPUBNavigatorViewController.Configurationto set the default user settings values (contributed by @ettore). - You can provide custom editing actions for the text selection menu (contributed by @cbaltzer).
- Create a custom action with, for example:
EditingAction(title: "Highlight", action: #selector(highlight:)) - Then, implement the selector in one of your classes in the responder chain. Typically, in the
UIViewControllerwrapping the navigator view controller.
class EPUBViewController: UIViewController { init(publication: Publication) { var config = EPUBNavigatorViewController.Configuration() config.editingActions.append(EditingAction(title: "Highlight", action: #selector(highlight))) let navigator = EPUBNavigatorViewController(publication: publication, config: config) } @objc func highlight(_ sender: Any) {} }
- Create a custom action with, for example:
- The EPUB navigator is now able to navigate to a
- New
SelectableNavigatorprotocol for navigators supporting user selection.- Get or clear the current selection.
- Implement
navigator(_:canPerformAction:for:)to validate each editing action for the current selection. For example, to make sure the selected text is not too large for a definition look up. - Implement
navigator(_:shouldShowMenuForSelection:)to override the default edit menu (UIMenuController) with a custom selection pop-up.
- (alpha) Support for the Decorator API to draw user interface elements over a publication's content.
- This can be used to render highlights over a text selection, for example.
- For now, only the EPUB navigator implements
DecorableNavigator. You can implement custom decoration styles withHTMLDecorationTemplate.
- (alpha) A new navigator for audiobooks.
- The navigator is chromeless, so you will need to provide your own user interface.
- Removed
navigator(_:userContentController:didReceive:)which is actually not needed since you can provide your ownWKScriptMessageHandlertoWKUserContentController.
- The default EPUB positions service now uses the archive entry length when available. This is similar to how Adobe RMSDK generates page numbers.
- To use the former strategy, create the
Streamerwith:Streamer(parsers: [EPUBParser(reflowablePositionsStrategy: .originalLength(pageLength: 1024))])
- To use the former strategy, create the
- #208 Crash when reading obfuscated EPUB resources with an empty publication identifier.
- Fixed receiving
EPUBNavigatorDelegate.navigator(_:setupUserScripts:)for the first web view. - r2-testapp-swift#343 Fixed hiding "Share" editing action (contributed by @rocxteady).
- #139 Compile error with Xcode 12.4
- All APIs deprecated in previous versions are now unavailable.
DownloadSessionis deprecated and will be removed in the next major version. Please migrate to your own download solution.
Resourcehas a new API to perform progressive asynchronous reads. This is useful when streaming a resource.HTTPFetcheris a new publication fetcher able to serve remote resources through HTTP.- The actual HTTP requests are performed with an instance of
HTTPClient.
- The actual HTTP requests are performed with an instance of
HTTPClientis a new protocol exposing a high level API to perform HTTP requests.- It supports simple fetches but also progressive downloads.
DefaultHTTPClientis an implementation ofHTTPClientusing standardURLSessionAPIs. You can use its delegate to customize how requests are created and even recover from errors, e.g. to implement Authentication for OPDS.- You can provide your own implementation of
HTTPClientto Readium APIs if you prefer to use a third-party networking library.
PublicationServiceContextnow holds a weak reference to the parentPublication. This can be used to access other services from a givenPublicationServiceimplementation.- The default
LocatorServiceimplementation can be used to get aLocatorfrom a global progression in the publication.publication.locate(progression: 0.5)
Streamertakes a new optionalHTTPClientdependency to handle HTTP requests.
- New
EPUBNavigatorDelegateAPIs to inject custom JavaScript.- Override
navigator(_:setupUserScripts:)to register additional user script to theWKUserContentControllerof each web view. - Override
navigator(_:userContentController:didReceive:)to receive callbacks from your scripts.
- Override
- The
ArchiveAPI now supports resource ownership at the entry level.- The default ZIP implementation takes advantage of this by opening a new ZIP stream for each resource to be served. This improves performances and memory safety.
- The HTTP server now requests that publication resources are not cached by browsers.
- Caching poses a security risk for protected publications.
- We removed the dependency to the private
R2LCPClient.framework, which means:- Now
r2-lcp-swiftworks as a Carthage dependency, no need to use a submodule anymore. - You do not need to modify
r2-lcp-swift'sCartfileanymore to add the privateliblcpdependency. - However, you must provide a facade to
LCPService(see README for an example implementation).
- Now
- The Renew Loan API got revamped to better support renewal through a web page.
- You will need to implement
LCPRenewDelegateto coordinate the UX interaction. - Readium ships with a default implementation
LCPDefaultRenewDelegateto handle web page renewal withSFSafariViewController.
- You will need to implement
- Improved performances when reading consecutive ranges of a deflated ZIP entry.
- HREF normalization when a resource path contains special characters.
- Optimized performances of preloaded EPUB resources.
- Fixed really slow opening of large PDF documents.
PublicationAssetis a new protocol which can be used to open a publication from various medium, such as a file, a remote URL or a custom source.Filewas replaced byFileAsset, which implementsPublicationAsset.
Formatgot merged intoMediaType, to simplify the media type APIs.- You can use
MediaType.of()to sniff the type of a file or bytes. MediaTypehas now optionalnameandfileExtensionproperties.- Some publication formats can be represented by several media type aliases. Using
mediaType.canonicalizedwill give you the canonical media type to use, for example when persisting the file type in a database. All Readium APIs are already returning canonical media types, so it only matters if you create aMediaTypeyourself from its string representation.
- You can use
ContentLayoutis deprecated, usepublication.metadata.effectiveReadingProgressionto determine the reading progression of a publication instead.
Streameris now expecting aPublicationAssetinstead of aFile. You can create custom implementations ofPublicationAssetto open a publication from different medium, such as a file, a remote URL, in-memory bytes, etc.FileAssetcan be used to replaceFileand provides the same behavior.
- EPUBs declaring multiple languages were laid out from right to left if the first language had an RTL reading
progression. Now if no reading progression is set, the
effectiveReadingProgressionwill be LTR.
- The Publication Services API allows to extend a
Publicationwith custom implementations of known services. This version ships with a few predefined services:PositionsServiceprovides a list of discrete locations in the publication, no matter what the original format is.CoverServiceprovides an easy access to a bitmap version of the publication cover.
- The Composite Fetcher API can be used to extend the way publication resources are accessed.
- Support for exploded directories for any archive-based publication format.
- Content Protection handles DRM and other format-specific protections in a more systematic way.
- LCP now ships an
LCPContentProtectionimplementation to be plugged into theStreamer. - You can add custom
ContentProtectionimplementations to support other DRMs by providing an instance to theStreamer.
- LCP now ships an
- A new
LinkRelationtype to represent link relations, instead of using raw strings.- This will improve code safety through type checking and enable code completion.
- Since
LinkRelationconforms toExpressibleByStringLiteral, you can continue using raw strings in the API. However, migrating your code is recommended, e.g.links.first(withRel: .cover). - Known link relations (including from OPDS specifications) are available under the
LinkRelationnamespace. You can easily add custom relations to the namespace by declaringstaticproperties in aLinkRelationextension.
- Streamer API offers a simple interface to parse a publication and replace standalone parsers.
- A generic
ImageParserfor bitmap-based archives (CBZ or exploded directories) and single image files. - A generic
AudioParserfor audio-based archives (Zipped Audio Book or exploded directories) and single audio files.
- Support for the new
Publicationmodel using the Content Protection for DRM rights and the Fetcher for resource access.- This replaces the
ContainerandDRMLicenseobjects which were needed by the navigator before.
- This replaces the
- LCP implementation of the Content Protection API to work with the new Streamer API.
- It is highly recommended that you upgrade to the new
StreamerAPI to open publications, which will simplify DRM unlocking.
- It is highly recommended that you upgrade to the new
- Two default implementations of
LCPAuthenticating:LCPDialogAuthenticationto prompt the user for its passphrase with the official LCP dialog.LCPPassphraseAuthenticationto provide directly a passphrase, pulled for example from a database or a web service.
LCPService.acquirePublication()is a new API to acquire a publication from a standalone license. Compared to the formerimportPublication():- It doesn't require the passphrase, to allow bulk imports.
- It can be cancelled by calling
cancel()on the returnedLCPAcquisitionobject.
LCPService.isLCPProtected()provides a way to check if a file is protected with LCP.
- The
PublicationandContainertypes were merged together to offer a single interface to a publication's resources.- Use
publication.get()to read the content of a resource, such as the cover. It will automatically be decrypted if aContentProtectionwas attached to thePublication.
- Use
ContainerandContentFilterswere replaced by a shared implementation of aFetcher.PDFFileParserwas replaced in favor of a sharedPDFDocumentprotocol. This version ships with two implementations using PDFKit and CoreGraphics.
LCPAuthenticatingis now provided with more information and you will need to update your implementation.
- Deobfuscating ranges of EPUB resources.
- Layout of right-to–left EPUB.
- Various EPUB navigation issues:
- Prevent breaking initial location when calling
updateUserSettingstoo soon. - Fix weird scrolling behavior when double tapping on the edges to turn pages.
- Don't send intermediate incorrect locators when loading a pending locator.
- Prevent breaking initial location when calling
- Optimize positions calculation for LCP protected PDF.
- The new Format API simplifies the detection of file formats, including known publication formats such as EPUB and PDF.
- A format can be "sniffed" from files, raw bytes or even HTTP responses.
- Reading apps are welcome to extend the API with custom formats.
- Using
Link.mediaType?.matches()is now recommended to safely check the type of a resource. - More details about the Swift implementation can be found in the pull request.
- In
Publicationshared models:- Presentation Hints and HTML Locations extensions.
- Support for OPDS holds, copies and availability in
Link, for library-specific features.
- (alpha) Audiobook toolkit:
AudioSessionsimplifies the setup of anAVAudioSessionand handling its interruptions.NowPlayingInfohelps manage the "Now Playing" information displayed on the lock screen.
ReadiumWebPubParserto parse all Readium Web Publication profiles, including Audiobooks and LCP for PDF. It parses both manifests and packages.
- Support for pop-up footnotes (contributed by @tooolbox).
- This is an opt-in feature. Reading apps can customize how footnotes are presented to the user by implementing
NavigatorDelegate.navigator(_:shouldNavigateToNoteAt:content:referrer:). An example presenting footnotes in pop-ups is demonstrated in the Test App. - Footnotes' content is extracted with scinfu/SwiftSoup, which you may need to add to your app if you're not using Carthage or CocoaPods.
- This is an opt-in feature. Reading apps can customize how footnotes are presented to the user by implementing
- In EPUB's user settings:
- Support for hyphenation (contributed by @ehapmgs).
- Publishers' default styles are now used by default.
- Default line height is increased to improve readability.
- JavaScript errors are logged in Xcode's console for easier debugging.
- Support for PDF and Readium Audiobooks protected with LCP.
- All the
Publicationshared models are now immutable, to improve code safety. This should not impact reading apps unless you createdPublicationor other models yourself. - The
DocumentTypesAPI was extended and offers an easy way to check if your app supports a given file. - Dependencies to format-related third-party libraries such as ZIP, XML and PDF are being consolidated into
r2-shared-swift. Therefore,r2-shared-swiftnow depends on Fuzi and ZIPFoundation. This change will improve maintainability by isolating third-party code and allow (work in progress) to substitute the default libraries with custom ones.
- Upgraded to Readium CSS 1.0.0-beta.1.
- Two new fonts are available: AccessibleDfa and IA Writer Duospace.
- The file structure now follows strictly the one from ReadiumCSS's
dist/, for easy upgrades and custom builds replacement.
LCPAuthenticatingcan now return hashed passphrases in addition to clear ones. This can be used by reading apps fetching hashed passphrases from a web service or Authentication for OPDS, for example.- Provided
LCPAuthenticatinginstances are now retained by the LCP service. Therefore, you can provide one without keeping a reference around in your own code.
- Significant performance improvement when opening PDF documents protected with LCP.
- Prevent the embedded HTTP server from stopping when the device is locked, to allow background playback of audiobooks.
- Jumping to a bookmark (
Locator) located in a resource that is not already pre-loaded used to fail for some publications. - Touching interactive elements in fixed-layout EPUBs, when two-page spreads are enabled.