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.
Warning: Features marked as alpha may change or be removed in a future release without notice. Use with caution.
- Positions computation, TTS and search is now enabled for Readium Web Publications conforming to the EPUB profile.
- New
VisualNavigatorDelegate
APIs 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 whenscroll
is 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
DirectionalNavigationAdapter
component helps you to turn pages with the arrows and space keyboard keys or taps on the edge of the screen.
PublicationServer
is deprecated. See the the migration guide to migrate the HTTP server.
- The EPUB
UserSettings
component is deprecated and replaced by the new Preferences API. Take a look at the user guide and migration guide.
- The
define
editing action replaceslookup
on 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 incorrectLocator
if the linktype
is 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
Locator
text ready for user display withlocator.text.sanitized()
. - A new
Publication.conforms(to:)
API to identify the profile of a publication. - Support for the
conformsTo
RWPM metadata, to identify the profile of aPublication
. - Support for right-to-left PDF documents by extracting the reading progression from the
ViewerPreferences/Direction
metadata. - HTTP client:
- A new
HTTPClient.download()
API to download HTTP resources to a temporary location. HTTPRequest
andDefaultHTTPClient
take an optionaluserAgent
property 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.format
is now deprecated in favor of the newPublication.conforms(to:)
API which is more accurate.- For example, replace
publication.format == .epub
withpublication.conforms(to: .epub)
before opening a publication with theEPUBNavigatorViewController
.
- For example, replace
- The
LCPService
now uses a providedHTTPClient
instance 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
translate
EPUB 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
LCPDialogViewController
with CocoaPods.
- Support for Swift Package Manager (contributed by @stevenzeck).
- (alpha) A new Publication
SearchService
to search through the resources' content with a default implementationStringSearchService
. Link
objects 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
Locator
using itstext
context. 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
userSettings
property forEPUBNavigatorViewController.Configuration
to 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
UIViewController
wrapping 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
SelectableNavigator
protocol 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 ownWKScriptMessageHandler
toWKUserContentController
.
- 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
Streamer
with: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.
DownloadSession
is deprecated and will be removed in the next major version. Please migrate to your own download solution.
Resource
has a new API to perform progressive asynchronous reads. This is useful when streaming a resource.HTTPFetcher
is 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
HTTPClient
is a new protocol exposing a high level API to perform HTTP requests.- It supports simple fetches but also progressive downloads.
DefaultHTTPClient
is an implementation ofHTTPClient
using standardURLSession
APIs. 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
HTTPClient
to Readium APIs if you prefer to use a third-party networking library.
PublicationServiceContext
now holds a weak reference to the parentPublication
. This can be used to access other services from a givenPublicationService
implementation.- The default
LocatorService
implementation can be used to get aLocator
from a global progression in the publication.publication.locate(progression: 0.5)
Streamer
takes a new optionalHTTPClient
dependency to handle HTTP requests.
- New
EPUBNavigatorDelegate
APIs to inject custom JavaScript.- Override
navigator(_:setupUserScripts:)
to register additional user script to theWKUserContentController
of each web view. - Override
navigator(_:userContentController:didReceive:)
to receive callbacks from your scripts.
- Override
- The
Archive
API 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-swift
works as a Carthage dependency, no need to use a submodule anymore. - You do not need to modify
r2-lcp-swift
'sCartfile
anymore to add the privateliblcp
dependency. - 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
LCPRenewDelegate
to coordinate the UX interaction. - Readium ships with a default implementation
LCPDefaultRenewDelegate
to 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.
PublicationAsset
is 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.File
was replaced byFileAsset
, which implementsPublicationAsset
.
Format
got merged intoMediaType
, to simplify the media type APIs.- You can use
MediaType.of()
to sniff the type of a file or bytes. MediaType
has now optionalname
andfileExtension
properties.- Some publication formats can be represented by several media type aliases. Using
mediaType.canonicalized
will 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 aMediaType
yourself from its string representation.
- You can use
ContentLayout
is deprecated, usepublication.metadata.effectiveReadingProgression
to determine the reading progression of a publication instead.
Streamer
is now expecting aPublicationAsset
instead of aFile
. You can create custom implementations ofPublicationAsset
to open a publication from different medium, such as a file, a remote URL, in-memory bytes, etc.FileAsset
can be used to replaceFile
and 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
effectiveReadingProgression
will be LTR.
- The Publication Services API allows to extend a
Publication
with custom implementations of known services. This version ships with a few predefined services:PositionsService
provides a list of discrete locations in the publication, no matter what the original format is.CoverService
provides 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
LCPContentProtection
implementation to be plugged into theStreamer
. - You can add custom
ContentProtection
implementations to support other DRMs by providing an instance to theStreamer
.
- LCP now ships an
- A new
LinkRelation
type to represent link relations, instead of using raw strings.- This will improve code safety through type checking and enable code completion.
- Since
LinkRelation
conforms 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
LinkRelation
namespace. You can easily add custom relations to the namespace by declaringstatic
properties in aLinkRelation
extension.
- Streamer API offers a simple interface to parse a publication and replace standalone parsers.
- A generic
ImageParser
for bitmap-based archives (CBZ or exploded directories) and single image files. - A generic
AudioParser
for audio-based archives (Zipped Audio Book or exploded directories) and single audio files.
- Support for the new
Publication
model using the Content Protection for DRM rights and the Fetcher for resource access.- This replaces the
Container
andDRMLicense
objects 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
Streamer
API to open publications, which will simplify DRM unlocking.
- It is highly recommended that you upgrade to the new
- Two default implementations of
LCPAuthenticating
:LCPDialogAuthentication
to prompt the user for its passphrase with the official LCP dialog.LCPPassphraseAuthentication
to 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 returnedLCPAcquisition
object.
LCPService.isLCPProtected()
provides a way to check if a file is protected with LCP.
- The
Publication
andContainer
types 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 aContentProtection
was attached to thePublication
.
- Use
Container
andContentFilters
were replaced by a shared implementation of aFetcher
.PDFFileParser
was replaced in favor of a sharedPDFDocument
protocol. This version ships with two implementations using PDFKit and CoreGraphics.
LCPAuthenticating
is 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
updateUserSettings
too 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
Publication
shared models:- Presentation Hints and HTML Locations extensions.
- Support for OPDS holds, copies and availability in
Link
, for library-specific features.
- (alpha) Audiobook toolkit:
AudioSession
simplifies the setup of anAVAudioSession
and handling its interruptions.NowPlayingInfo
helps manage the "Now Playing" information displayed on the lock screen.
ReadiumWebPubParser
to 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
Publication
shared models are now immutable, to improve code safety. This should not impact reading apps unless you createdPublication
or other models yourself. - The
DocumentTypes
API 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-swift
now 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.
LCPAuthenticating
can 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
LCPAuthenticating
instances 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.