Skip to content

Commit 1ba1223

Browse files
authored
Small changes for v3 and guide to open a publication (readium#432)
1 parent 2f04d5e commit 1ba1223

File tree

17 files changed

+214
-176
lines changed

17 files changed

+214
-176
lines changed

docs/guides/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# User guides
22

3+
* [Opening a publication](open-publication.md)
34
* [Extracting the content of a publication](content.md)
45
* [Supporting PDF documents](pdf.md)
56
* [Configuring the Navigator](navigator-preferences.md)

docs/guides/open-publication.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Opening a publication
2+
3+
:warning: The described components are is still experimental.
4+
5+
Readium requires you to instantiate a few components before you can actually open a publication.
6+
7+
## Constructing an `AssetOpener`
8+
9+
First, you need to instantiate an `HttpClient` to provide the toolkit the ability to do HTTP requests.
10+
You can use the Readium `DefaultHttpClient` or a custom implementation. In the former case, its callback will
11+
enable you to perform authentication when required.
12+
Then, you can create an `AssetOpener` which will enable you to read content through different schemes and guessing its format.
13+
In addition to an `HttpClient`, the `AssetOpener` constructor takes a `ContentResolver` to support data access through the `content` scheme.
14+
15+
```kotlin
16+
val httpClient = DefaultHttpClient()
17+
18+
val assetOpener = AssetOpener(context.contentResolver, httpClient)
19+
```
20+
21+
## Constructing a `PublicationOpener`
22+
23+
The component which can parse an `Asset` giving access to a publication to build a proper `Publication`
24+
object is the `PublicationOpener`. Its constructor requires you to pass in:
25+
26+
* a `PublicationParser` it delegates the parsing work to.
27+
* a list of `ContentProtection`s which will deal with DRMs.
28+
29+
The easiest way to get a `PublicationParser` is to use the `DefaultPublicationParser` class. As to
30+
`ContentProtection`s, you can get one to support LCP publications through your `LcpService` if you have one.
31+
32+
```kotlin
33+
val contentProtections = listOf(lcpService.contentProtection(authentication))
34+
35+
val publicationParser = DefaultPublicationParser(context, httpClient, assetOpener, pdfFactory)
36+
37+
val publicationOpener = PublicationOpener(publicationParser, contentProtections)
38+
```
39+
40+
## Bringing the pieces together
41+
42+
Once you have got an `AssetOpener` and a `PublicationOpener`, you can eventually open a publication as follows:
43+
```kotlin
44+
val asset = assetOpener.open(url, mediaType)
45+
.getOrElse { return error }
46+
47+
val publication = publicationOpener.open(asset)
48+
.getOrElse { return error }
49+
```
50+
51+
Persisting the asset media type on the device can significantly improve performance as it is valuable hint
52+
for the content format, especially in case of remote publications.
53+
54+
## Extensibility`
55+
56+
`DefaultPublicationParser` accepts additional parsers. You can also use your own parser list
57+
with `CompositePublicationParser` or implement [PublicationParser] in the way you like.
58+
59+
`AssetOpener` offers an alternative constructor providing better extensibility in a similar way.
60+
This constructor takes several parameters with different responsibilities.
61+
62+
* `ResourceFactory` determines which schemes you will be able to access content through.
63+
* `ArchiveOpener` which kinds of archives your `AssetOpener` will be able to open.
64+
* `FormatSniffer` which file formats your `AssetOpener` will be able to identify.
65+
66+
For each of these components, you can either use the default implementations or implement yours
67+
with the composite pattern. `CompositeResourceFactory`, `CompositeArchiveOpener` and `CompositeFormatSniffer`
68+
provide simple implementations trying every item of a list in turns.
69+

readium/shared/src/main/java/org/readium/r2/shared/util/Error.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public fun Error.toDebugDescription(): String =
5656
} else {
5757
var desc = "${javaClass.nameWithEnclosingClasses()}: $message"
5858
cause?.let { cause ->
59-
desc += "\n\n${cause.toDebugDescription()}"
59+
desc += "\n${cause.toDebugDescription()}"
6060
}
6161
desc
6262
}
@@ -67,7 +67,7 @@ private fun Throwable.toDebugDescription(): String {
6767
desc += message ?: ""
6868
desc += "\n" + stackTrace.take(2).joinToString("\n").prependIndent(" ")
6969
cause?.let { cause ->
70-
desc += "\n\n${cause.toDebugDescription()}"
70+
desc += "\n${cause.toDebugDescription()}"
7171
}
7272
return desc
7373
}

readium/shared/src/main/java/org/readium/r2/shared/util/asset/AssetOpener.kt

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,22 @@
66

77
package org.readium.r2.shared.util.asset
88

9+
import android.content.ContentResolver
910
import java.io.File
1011
import org.readium.r2.shared.InternalReadiumApi
1112
import org.readium.r2.shared.util.AbsoluteUrl
1213
import org.readium.r2.shared.util.Error
1314
import org.readium.r2.shared.util.Try
1415
import org.readium.r2.shared.util.Url
15-
import org.readium.r2.shared.util.file.FileResourceFactory
16-
import org.readium.r2.shared.util.format.DefaultFormatSniffer
1716
import org.readium.r2.shared.util.format.Format
1817
import org.readium.r2.shared.util.format.FormatHints
1918
import org.readium.r2.shared.util.format.FormatSniffer
2019
import org.readium.r2.shared.util.getOrElse
20+
import org.readium.r2.shared.util.http.HttpClient
2121
import org.readium.r2.shared.util.mediatype.MediaType
2222
import org.readium.r2.shared.util.resource.Resource
2323
import org.readium.r2.shared.util.resource.ResourceFactory
2424
import org.readium.r2.shared.util.toUrl
25-
import org.readium.r2.shared.util.zip.ZipArchiveOpener
2625

2726
/**
2827
* Retrieves an [Asset] instance providing reading access to the resource(s) of an asset stored at
@@ -34,11 +33,20 @@ public class AssetOpener(
3433
private val archiveOpener: ArchiveOpener
3534
) {
3635
public constructor(
37-
resourceFactory: ResourceFactory = FileResourceFactory(),
38-
archiveOpener: ArchiveOpener = ZipArchiveOpener(),
39-
formatSniffer: FormatSniffer = DefaultFormatSniffer()
36+
resourceFactory: ResourceFactory,
37+
archiveOpener: ArchiveOpener,
38+
formatSniffer: FormatSniffer
4039
) : this(AssetSniffer(formatSniffer, archiveOpener), resourceFactory, archiveOpener)
4140

41+
public constructor(
42+
contentResolver: ContentResolver,
43+
httpClient: HttpClient
44+
) : this(
45+
DefaultResourceFactory(contentResolver, httpClient),
46+
DefaultArchiveOpener(),
47+
DefaultFormatSniffer()
48+
)
49+
4250
public sealed class OpenError(
4351
override val message: String,
4452
override val cause: Error?

readium/shared/src/main/java/org/readium/r2/shared/util/asset/AssetSniffer.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import org.readium.r2.shared.util.data.Container
1616
import org.readium.r2.shared.util.data.ReadError
1717
import org.readium.r2.shared.util.data.Readable
1818
import org.readium.r2.shared.util.file.FileResource
19-
import org.readium.r2.shared.util.format.DefaultFormatSniffer
2019
import org.readium.r2.shared.util.format.Format
2120
import org.readium.r2.shared.util.format.FormatHints
2221
import org.readium.r2.shared.util.format.FormatSniffer
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright 2023 Readium Foundation. All rights reserved.
3+
* Use of this source code is governed by the BSD-style license
4+
* available in the top-level LICENSE file of the project.
5+
*/
6+
7+
package org.readium.r2.shared.util.asset
8+
9+
import android.content.ContentResolver
10+
import org.readium.r2.shared.util.content.ContentResourceFactory
11+
import org.readium.r2.shared.util.file.FileResourceFactory
12+
import org.readium.r2.shared.util.format.ArchiveSniffer
13+
import org.readium.r2.shared.util.format.AudioSniffer
14+
import org.readium.r2.shared.util.format.BitmapSniffer
15+
import org.readium.r2.shared.util.format.CompositeFormatSniffer
16+
import org.readium.r2.shared.util.format.EpubDrmSniffer
17+
import org.readium.r2.shared.util.format.EpubSniffer
18+
import org.readium.r2.shared.util.format.FormatSniffer
19+
import org.readium.r2.shared.util.format.HtmlSniffer
20+
import org.readium.r2.shared.util.format.JsonSniffer
21+
import org.readium.r2.shared.util.format.LcpLicenseSniffer
22+
import org.readium.r2.shared.util.format.LpfSniffer
23+
import org.readium.r2.shared.util.format.Opds1Sniffer
24+
import org.readium.r2.shared.util.format.Opds2Sniffer
25+
import org.readium.r2.shared.util.format.PdfSniffer
26+
import org.readium.r2.shared.util.format.RarSniffer
27+
import org.readium.r2.shared.util.format.RpfSniffer
28+
import org.readium.r2.shared.util.format.RwpmSniffer
29+
import org.readium.r2.shared.util.format.W3cWpubSniffer
30+
import org.readium.r2.shared.util.format.ZipSniffer
31+
import org.readium.r2.shared.util.http.HttpClient
32+
import org.readium.r2.shared.util.http.HttpResourceFactory
33+
import org.readium.r2.shared.util.resource.CompositeResourceFactory
34+
import org.readium.r2.shared.util.resource.ResourceFactory
35+
import org.readium.r2.shared.util.zip.ZipArchiveOpener
36+
37+
/**
38+
* Default implementation of [ResourceFactory] supporting file, content and http schemes.
39+
*
40+
* @param contentResolver content resolver to use to support content scheme.
41+
* @param httpClient Http client to use to support http scheme.
42+
* @param additionalFactories Additional [ResourceFactory] to support additional schemes.
43+
*/
44+
public class DefaultResourceFactory(
45+
contentResolver: ContentResolver,
46+
httpClient: HttpClient,
47+
additionalFactories: List<ResourceFactory> = emptyList()
48+
) : ResourceFactory by CompositeResourceFactory(
49+
*additionalFactories.toTypedArray(),
50+
FileResourceFactory(),
51+
ContentResourceFactory(contentResolver),
52+
HttpResourceFactory(httpClient)
53+
)
54+
55+
/**
56+
* Default implementation of [ArchiveOpener] supporting only ZIP archives.
57+
*
58+
* @param additionalOpeners Additional openers to be used.
59+
*/
60+
public class DefaultArchiveOpener(
61+
additionalOpeners: List<ArchiveOpener> = emptyList()
62+
) : ArchiveOpener by CompositeArchiveOpener(
63+
*additionalOpeners.toTypedArray(),
64+
ZipArchiveOpener()
65+
)
66+
67+
/**
68+
* Default implementation of [FormatSniffer] guessing as well as possible all formats known by
69+
* Readium.
70+
*
71+
* @param additionalSniffers Additional sniffers to be used to guess content format.
72+
*/
73+
public class DefaultFormatSniffer(
74+
additionalSniffers: List<FormatSniffer> = emptyList()
75+
) : FormatSniffer by CompositeFormatSniffer(
76+
*additionalSniffers.toTypedArray(),
77+
ZipSniffer,
78+
RarSniffer,
79+
EpubSniffer,
80+
LpfSniffer,
81+
ArchiveSniffer,
82+
RpfSniffer,
83+
PdfSniffer,
84+
HtmlSniffer,
85+
BitmapSniffer,
86+
AudioSniffer,
87+
JsonSniffer,
88+
Opds1Sniffer,
89+
Opds2Sniffer,
90+
LcpLicenseSniffer,
91+
EpubDrmSniffer,
92+
W3cWpubSniffer,
93+
RwpmSniffer
94+
)

readium/shared/src/main/java/org/readium/r2/shared/util/format/DefaultSniffers.kt renamed to readium/shared/src/main/java/org/readium/r2/shared/util/format/Sniffers.kt

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,6 @@ import org.readium.r2.shared.util.getOrDefault
2929
import org.readium.r2.shared.util.getOrElse
3030
import org.readium.r2.shared.util.mediatype.MediaType
3131

32-
public class DefaultFormatSniffer :
33-
FormatSniffer by CompositeFormatSniffer(
34-
ZipSniffer,
35-
RarSniffer,
36-
EpubSniffer,
37-
LpfSniffer,
38-
ArchiveSniffer,
39-
RpfSniffer,
40-
PdfSniffer,
41-
HtmlSniffer,
42-
BitmapSniffer,
43-
AudioSniffer,
44-
JsonSniffer,
45-
Opds1Sniffer,
46-
Opds2Sniffer,
47-
LcpLicenseSniffer,
48-
EpubDrmSniffer,
49-
W3cWpubSniffer,
50-
RwpmSniffer
51-
)
52-
5332
/** Sniffs an HTML or XHTML document. */
5433
public object HtmlSniffer : FormatSniffer {
5534
override fun sniffHints(

readium/shared/src/main/java/org/readium/r2/shared/util/http/DefaultHttpClient.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import java.net.NoRouteToHostException
1717
import java.net.SocketTimeoutException
1818
import java.net.URL
1919
import java.net.UnknownHostException
20+
import javax.net.ssl.SSLHandshakeException
2021
import kotlin.time.Duration
2122
import kotlinx.coroutines.Dispatchers
2223
import kotlinx.coroutines.withContext
@@ -327,6 +328,8 @@ private fun wrap(cause: IOException): HttpError =
327328
HttpError.Unreachable(ThrowableError(cause))
328329
is SocketTimeoutException ->
329330
HttpError.Timeout(ThrowableError(cause))
331+
is SSLHandshakeException ->
332+
HttpError.SslHandshake(ThrowableError(cause))
330333
else ->
331334
HttpError.IO(cause)
332335
}

readium/shared/src/main/java/org/readium/r2/shared/util/http/HttpError.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,26 @@ public sealed class HttpError(
2121
public override val cause: Error? = null
2222
) : AccessError {
2323

24+
/** Malformed HTTP response. */
2425
public class MalformedResponse(cause: Error?) :
2526
HttpError("The received response could not be decoded.", cause)
2627

2728
/** The client, server or gateways timed out. */
2829
public class Timeout(cause: Error) :
2930
HttpError("Request timed out.", cause)
3031

32+
/** Server could not be reached. */
3133
public class Unreachable(cause: Error) :
3234
HttpError("Server could not be reached.", cause)
3335

36+
/** Redirection failed. */
3437
public class Redirection(cause: Error) :
3538
HttpError("Redirection failed.", cause)
3639

40+
/** SSL Handshake failed. */
41+
public class SslHandshake(cause: Error) :
42+
HttpError("SSL handshake failed.", cause)
43+
3744
/** An unknown networking error. */
3845
public class IO(cause: Error) :
3946
HttpError("An IO error occurred.", cause) {
@@ -42,6 +49,8 @@ public sealed class HttpError(
4249
}
4350

4451
/**
52+
* Server responded with an error status code.
53+
*
4554
* @param status HTTP status code.
4655
* @param mediaType Response media type.
4756
* @param body Response body.

readium/shared/src/main/java/org/readium/r2/shared/util/http/HttpURLConnectionExt.kt

Lines changed: 0 additions & 23 deletions
This file was deleted.

0 commit comments

Comments
 (0)