Skip to content

Commit 81cb0db

Browse files
authored
Documentation (#384)
1 parent 27668a9 commit 81cb0db

File tree

13 files changed

+914
-14
lines changed

13 files changed

+914
-14
lines changed

CONTRIBUTING.md

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Contributing to the Readium Swift Toolkit
2+
3+
First and foremost, thanks for your interest! 🙏 We need contributors like you to help bring this project to fruition.
4+
5+
We welcome many kind of contributions such as improving the documentation, submitting bug reports and feature requests, or writing code.
6+
7+
## Writing code
8+
9+
### Coding standard
10+
11+
We use [`SwiftFormat`](https://github.com/nicklockwood/SwiftFormat) to ensure code formatting and avoid bikeshedding.
12+
13+
Before submitting a PR, save yourself some trouble by automatically formatting the code with `make format` from the project's root directory.
14+
15+
### Modifying the EPUB Navigator's JavaScript layer
16+
17+
The EPUB navigator injects a set of JavaScript files into a publication's resources, exposing a JavaScript API to the `WKWebView` under the `readium` global namespace. The JavaScript source code is located under [`Sources/Navigator/EPUB/Scripts`](Sources/Navigator/EPUB/Scripts).
18+
19+
`index-reflowable.js` is the root of the bundle injected in a reflowable EPUB's resources, while `index-fixed.js` is used for a fixed-layout EPUB's resources.
20+
21+
In the case of fixed-layout EPUBs, the publication resources are actually loaded inside an `iframe` in one of [our HTML wrapper pages](Sources/Navigator/EPUB/Assets/) (`fxl-spread-one.html` for single pages, `fxl-spread-two.html` when displaying two pages side-by-side). The matching `index-fixed-wrapper-one.js` and `index-fixed-wrapper-two.js` are injected in the HTML wrappers.
22+
23+
If you make any changes to the JavaScript files, you must regenerate the bundles embedded in the application. First, make sure you have [`corepack` installed](https://pnpm.io/installation#using-corepack). Then, run `make scripts` from the project's root directory.
24+
+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Getting started
2+
3+
The Readium Swift toolkit enables you to develop reading apps for iOS and iPadOS. It provides built-in support for multiple publication formats such as EPUB, PDF, audiobooks, and comics.
4+
5+
:warning: Readium offers only low-level tools. You are responsible for creating a user interface for reading and managing books, as well as a data layer to store the user's publications. The Test App is an example of such integration.
6+
7+
## Design principles
8+
9+
The toolkit has been designed following these core tenets:
10+
11+
* **Modular**: It is divided into separate modules that can be used independently.
12+
* **Extensible**: Integrators should be able to support a custom DRM, publication format or inject their own stylesheets without modifying the toolkit itself.
13+
* **Opiniated**: We adhere to open standards but sometimes interpret them for practicality.
14+
15+
## Packages
16+
17+
### Main packages
18+
19+
* `R2Shared` contains shared `Publication` models and utilities.
20+
* `R2Streamer` parses publication files (e.g. an EPUB) into a `Publication` object.
21+
* [`R2Navigator` renders the content of a publication](Navigator/Navigator.md).
22+
23+
### Specialized packages
24+
25+
* `ReadiumOPDS` parses [OPDS catalog feeds](https://opds.io) (both OPDS 1 and 2).
26+
* [`ReadiumLCP` downloads and decrypts LCP-protected publications](Readium%20LCP.md).
27+
28+
### Adapters to third-party dependencies
29+
30+
* `ReadiumAdapterGCDWebServer` provides an HTTP server built with [GCDWebServer](https://github.com/swisspol/GCDWebServer).
31+
32+
## Overview of the shared models (`R2Shared`)
33+
34+
The Readium toolkit provides models used as exchange types between packages.
35+
36+
### Publication models
37+
38+
#### Publication
39+
40+
`Publication` and its sub-components represent a single publication – ebook, audiobook or comic. It is loosely based on the [Readium Web Publication Manifest](https://readium.org/webpub-manifest/).
41+
42+
A `Publication` instance:
43+
44+
* holds the metadata of a publication, such as its author or table of contents,
45+
* allows to read the contents of a publication, e.g. XHTML or audio resources,
46+
* provides additional services, for example content extraction or text search.
47+
48+
#### Link
49+
50+
51+
A [`Link` object](https://readium.org/webpub-manifest/#24-the-link-object) holds a pointer (URL) to a resource or service along with additional metadata, such as its media type or title.
52+
53+
The `Publication` contains several `Link` collections, for example:
54+
55+
* `readingOrder` lists the publication resources arranged in the order they should be read.
56+
* `resources` contains secondary resources necessary for rendering the `readingOrder`, such as an image or a font file.
57+
* `tableOfContents` represents the table of contents as a tree of `Link` objects.
58+
* `links` exposes additional resources, such as a canonical link to the manifest or a search web service.
59+
60+
#### Locator
61+
62+
A [`Locator` object](https://readium.org/architecture/models/locators/) represents a precise location in a publication resource in a format that can be stored and shared across reading systems. It is more accurate than a `Link` and contains additional information about the location, e.g. progression percentage, position or textual context.
63+
64+
`Locator` objects are used for various features, including:
65+
66+
* reporting the current progression in the publication
67+
* saving bookmarks, highlights and annotations
68+
* navigating search results
69+
70+
### Data models
71+
72+
#### Publication Asset
73+
74+
A `PublicationAsset` is an interface representing a single file or package holding the content of a `Publication`. A default implementation `FileAsset` grants access to a publication stored locally.
75+
76+
#### Resource
77+
78+
A `Resource` provides read access to a single resource of a publication, such as a file or an entry in an archive.
79+
80+
`Resource` instances are usually created by a `Fetcher`. The toolkit ships with various implementations supporting different data access protocols such as local files, HTTP, etc.
81+
82+
#### Fetcher
83+
84+
A `Fetcher` provides read access to a collection of resources. `Fetcher` instances are created by a `PublicationAsset` to provide access to the content of a publication.
85+
86+
`Publication` objects internally use a `Fetcher` to expose their content.
87+
88+
## Opening a publication (`R2Streamer`)
89+
90+
To retrieve a `Publication` object from a publication file like an EPUB or audiobook, begin by creating a `PublicationAsset` object used to read the file. Readium provides a `FileAsset` implementation for reading a publication stored on the local file system.
91+
92+
```swift
93+
let file = URL(fileURLWithPath: "path/to/book.epub")
94+
let asset = FileAsset(file: file)
95+
```
96+
97+
Then, use a `Streamer` instance to parse the asset and create a `Publication` object.
98+
99+
```swift
100+
let streamer = Streamer()
101+
102+
streamer.open(asset: asset, allowUserInteraction: false) { result in
103+
switch result {
104+
case .success(let publication):
105+
print("Opened \(publication.metadata.title)")
106+
case .failure(let error):
107+
alert(error.localizedDescription)
108+
case .cancelled:
109+
// The user cancelled the opening, for example by dismissing a password pop-up.
110+
break
111+
}
112+
}
113+
```
114+
115+
The `allowUserInteraction` parameter is useful when supporting a DRM like Readium LCP. It indicates if the toolkit can prompt the user for credentials when the publication is protected.
116+
117+
## Accessing the metadata of a publication
118+
119+
After opening a publication, you may want to read its metadata to insert a new entity into your bookshelf database, for instance. The `publication.metadata` object contains everything you need, including `title`, `authors` and the `published` date.
120+
121+
You can retrieve the publication cover using `publication.cover`. Avoid calling this from the main thread to prevent blocking the user interface.
122+
123+
## Rendering the publication on the screen (`R2Navigator`)
124+
125+
You can use a Readium navigator to present the publication to the user. The `Navigator` renders resources on the screen and offers APIs and user interactions for navigating the contents.
126+
127+
```swift
128+
let navigator = try EPUBNavigatorViewController(
129+
publication: publication,
130+
initialLocation: lastReadLocation,
131+
httpServer: GCDHTTPServer.shared
132+
)
133+
134+
hostViewController.present(navigator, animated: true)
135+
```
136+
Please refer to the [Navigator guide](Navigator/Navigator.md) for more information.
+194
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# Navigator
2+
3+
You can use a Readium Navigator to present the publication to the user. The `Navigator` renders resources on the screen and offers APIs and user interactions for navigating the contents.
4+
5+
:warning: Navigators do not have user interfaces besides the view that displays the publication. Applications are responsible for providing a user interface with bookmark buttons, a progress bar, etc.
6+
7+
## Default implementations
8+
9+
The Readium toolkit comes with several `Navigator` implementations for different publication profiles. Some are `UIViewController`s, designed to be added to your view hierarchy, while others are chromeless and can be used in the background.
10+
11+
| Navigator | Profile | Formats |
12+
|-------------------------------|-------------|-----------------------------------------------------------------------|
13+
| `EPUBNavigatorViewController` | `epub` | EPUB (`.epub`), Readium Web Publication (`.webpub`) |
14+
| `PDFNavigatorViewController` | `pdf` | PDF (`.pdf`), LCP-protected PDF (`.lcpdf`) |
15+
| `CBZNavigatorViewController` | `divina` | Zipped Comic Book (`cbz`), Readium Divina (`.divina`) |
16+
| `AudioNavigator` | `audiobook` | Zipped Audio Book (`.zab`), Readium Audiobook (`.audiobook`, `.lcpa`) |
17+
18+
To find out which Navigator is compatible with a publication, refer to its [profile](https://readium.org/webpub-manifest/profiles/). Use `publication.conformsTo()` to identify the publication's profile.
19+
20+
```swift
21+
if publication.conformsTo(.epub) {
22+
let navigator = try EPUBNavigatorViewController(
23+
publication: publication,
24+
initialLocation: lastReadLocation,
25+
httpServer: GCDHTTPServer.shared
26+
)
27+
28+
hostViewController.present(navigator, animated: true)
29+
}
30+
```
31+
32+
## Navigator APIs
33+
34+
Navigators implement a set of shared interfaces to help reuse the reading logic across publication profiles. For example, instead of using specific implementations like `EPUBNavigatorViewController`, use the `Navigator` interface to create a location history manager compatible with all Navigator types.
35+
36+
You can create custom Navigators and easily integrate them into your app with minimal modifications by implementing these interfaces.
37+
38+
### `Navigator` interface
39+
40+
All Navigators implement the `Navigator` interface, which provides the foundation for navigating resources in a `Publication`. You can use it to move through the publication's content or to find the current position.
41+
42+
Note that this interface does not specify how the content is presented to the user.
43+
44+
### `VisualNavigator` interface
45+
46+
Navigators rendering the content visually on the screen implement the `VisualNavigator` interface. This interface offers details about the presentation style (e.g., scrolled, right-to-left, etc.) and allows monitoring input events like taps or keyboard strokes.
47+
48+
### `SelectableNavigator` interface
49+
50+
Navigators enabling users to select parts of the content implement `SelectableNavigator`. You can use it to extract the `Locator` and content of the selected portion.
51+
52+
### `DecorableNavigator` interface
53+
54+
A Decorable Navigator is able to render decorations over a publication, such as highlights or margin icons.
55+
56+
[See the corresponding proposal for more information](https://readium.org/architecture/proposals/008-decorator-api.html).
57+
58+
## Instantiating a Navigator
59+
60+
### Visual Navigators
61+
62+
The Visual Navigators are implemented as `UIViewController` and must be added to your iOS view hierarchy to render the publication contents.
63+
64+
```swift
65+
let navigator = try EPUBNavigatorViewController(
66+
publication: publication,
67+
initialLocation: lastReadLocation,
68+
httpServer: GCDHTTPServer.shared
69+
)
70+
71+
hostViewController.present(navigator, animated: true)
72+
```
73+
74+
:point_up: The HTTP server is used to serve the publication resources to the Navigator. You may use your own implementation, or the recommended `GCDHTTPServer` which is part of the `ReadiumAdapterGCDWebServer` package.
75+
76+
### Audio Navigator
77+
78+
The `AudioNavigator` is chromeless and does not provide any user interface, allowing you to create your own custom UI.
79+
80+
```swift
81+
let navigator = AudioNavigator(
82+
publication: publication,
83+
initialLocation: lastReadLocation
84+
)
85+
86+
navigator.play()
87+
```
88+
89+
## Navigating the contents of the publication
90+
91+
The `Navigator` interface offers various `go` APIs for navigating the publication. For instance:
92+
93+
* to the previous or next pages: `navigator.goForward()` or `navigator.goBackward()`
94+
* to a link from the `publication.tableOfContents` or `publication.readingOrder`: `navigator.go(to: link)`
95+
* to a locator from a search result: `navigator.go(to: locator)`
96+
97+
## Reading progression
98+
99+
### Saving and restoring the last read location
100+
101+
Navigators don't store any data permanently. Therefore, it is your responsibility to save the last read location in your database and restore it when creating a new Navigator.
102+
103+
You can observe the current position in the publication by implementing a `NavigatorDelegate`.
104+
105+
```swift
106+
navigator.delegate = MyNavigatorDelegate()
107+
108+
class MyNavigatorDelegate: NavigatorDelegate {
109+
110+
override func navigator(_ navigator: Navigator, locationDidChange locator: Locator) {
111+
if let position = locator.locations.position {
112+
print("At position \(position) on \(publication.positions.count)")
113+
}
114+
if let progression = locator.locations.progression {
115+
return "Progression in the current resource: \(progression)%"
116+
}
117+
if let totalProgression = locator.locations.totalProgression {
118+
return "Total progression in the publication: \(progression)%"
119+
}
120+
121+
// Save the position in your bookshelf database
122+
database.saveLastReadLocation(locator.jsonString)
123+
}
124+
}
125+
```
126+
127+
The `Locator` object may be serialized to JSON in your database, and deserialized to set the initial location when creating the navigator.
128+
129+
```swift
130+
let lastReadLocation = Locator(jsonString: dabase.lastReadLocation())
131+
132+
let navigator = try EPUBNavigatorViewController(
133+
publication: publication,
134+
initialLocation: lastReadLocation,
135+
httpServer: GCDHTTPServer.shared
136+
)
137+
```
138+
139+
### Bookmarking the current location
140+
141+
Use a Navigator's `currentLocation` property to persists the current position, for instance as a bookmark.
142+
143+
After the user selects a bookmark from your user interface, navigate to it using `navigator.go(bookmark.locator)`.
144+
145+
### Displaying a progression slider
146+
147+
To display a percentage-based progression slider, use the `locations.totalProgression` property of the `currentLocation`. This property holds the total progression across an entire publication.
148+
149+
Given a progression from 0 to 1, you can obtain a `Locator` object from the `Publication`. This can be used to navigate to a specific percentage within the publication.
150+
151+
```swift
152+
if let locator = publication.locate(progression: 0.5) {
153+
navigator.go(to: locator)
154+
}
155+
```
156+
157+
### Displaying the number of positions
158+
159+
:warning: Readium does not have the concept of pages, as they are not useful when dealing with reflowable publications across different screen sizes. Instead, we use [**positions**](https://readium.org/architecture/models/locators/positions/) which remain stable even when the user changes the font size or device.
160+
161+
Not all Navigators provide positions, but most `VisualNavigator` implementations do. Verify if `publication.positions` is not empty to determine if it is supported.
162+
163+
To find the total positions in the publication, use `publication.positions.count`. You can get the current position with `navigator.currentLocation?.locations.position`.
164+
165+
## Navigating with edge taps and keyboard arrows
166+
167+
Readium provides a `DirectionalNavigationAdapter` helper to turn pages using arrow and space keys or screen taps.
168+
169+
You can use it from your `VisualNavigatorDelegate` implementation:
170+
171+
```swift
172+
extension MyReader: VisualNavigatorDelegate {
173+
174+
func navigator(_ navigator: VisualNavigator, didTapAt point: CGPoint) {
175+
// Turn pages when tapping the edge of the screen.
176+
guard !DirectionalNavigationAdapter(navigator: navigator).didTap(at: point) else {
177+
return
178+
}
179+
180+
toggleNavigationBar()
181+
}
182+
183+
func navigator(_ navigator: VisualNavigator, didPressKey event: KeyEvent) {
184+
// Turn pages when pressing the arrow keys.
185+
DirectionalNavigationAdapter(navigator: navigator).didPressKey(event: event)
186+
}
187+
}
188+
```
189+
190+
`DirectionalNavigationAdapter` offers a lot of customization options. Take a look at its API.
191+
192+
## User preferences
193+
194+
Readium Navigators support user preferences, such as font size or background color. Take a look at [the Preferences API guide](Preferences.md) for more information.

Documentation/Guides/Navigator Preferences.md renamed to Documentation/Guides/Navigator/Preferences.md

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# Configuring the Navigator
22

3-
:warning: The Navigator Setting API is brand new and currently only available with `EPUBNavigatorViewController` and `PDFNavigatorViewController`.
4-
53
Take a look at the [migration guide](../Migration%20Guide.md) if you are already using the legacy EPUB settings.
64

75
## Overview

0 commit comments

Comments
 (0)