From 94fe656b80ccf645941371c57f1161953cd65dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Tue, 9 Jan 2024 12:27:01 +0100 Subject: [PATCH 1/2] Simplify the `PublicationOpener` by adding a `DefaultPublicationParser` --- .../main/java/org/readium/r2/shared/OptIn.kt | 13 ---- .../readium/r2/streamer/PublicationOpener.kt | 62 ++---------------- .../r2/streamer/parser/PublicationParser.kt | 64 ++++++++++++++++++- .../r2/streamer/parser/pdf/PdfParser.kt | 2 - .../java/org/readium/r2/testapp/Readium.kt | 17 +++-- 5 files changed, 77 insertions(+), 81 deletions(-) diff --git a/readium/shared/src/main/java/org/readium/r2/shared/OptIn.kt b/readium/shared/src/main/java/org/readium/r2/shared/OptIn.kt index 0107964486..ae95dc85cb 100644 --- a/readium/shared/src/main/java/org/readium/r2/shared/OptIn.kt +++ b/readium/shared/src/main/java/org/readium/r2/shared/OptIn.kt @@ -49,19 +49,6 @@ public annotation class ExperimentalReadiumApi ) public annotation class DelicateReadiumApi -@RequiresOptIn( - level = RequiresOptIn.Level.WARNING, - message = "Support for PDF is still experimental. The API may be changed in the future without notice." -) -@Retention(AnnotationRetention.BINARY) -@Target( - AnnotationTarget.CLASS, - AnnotationTarget.FUNCTION, - AnnotationTarget.TYPEALIAS, - AnnotationTarget.PROPERTY -) -public annotation class PdfSupport - @RequiresOptIn( level = RequiresOptIn.Level.WARNING, message = "Support for SearchService is still experimental. The API may be changed in the future without notice." diff --git a/readium/streamer/src/main/java/org/readium/r2/streamer/PublicationOpener.kt b/readium/streamer/src/main/java/org/readium/r2/streamer/PublicationOpener.kt index ef2b050154..597daf2a30 100644 --- a/readium/streamer/src/main/java/org/readium/r2/streamer/PublicationOpener.kt +++ b/readium/streamer/src/main/java/org/readium/r2/streamer/PublicationOpener.kt @@ -6,8 +6,6 @@ package org.readium.r2.streamer -import android.content.Context -import org.readium.r2.shared.PdfSupport import org.readium.r2.shared.publication.Publication import org.readium.r2.shared.publication.protection.ContentProtection import org.readium.r2.shared.publication.protection.FallbackContentProtection @@ -15,46 +13,22 @@ import org.readium.r2.shared.util.DebugError import org.readium.r2.shared.util.Error import org.readium.r2.shared.util.Try import org.readium.r2.shared.util.asset.Asset -import org.readium.r2.shared.util.asset.AssetOpener import org.readium.r2.shared.util.data.ReadError import org.readium.r2.shared.util.getOrElse -import org.readium.r2.shared.util.http.HttpClient import org.readium.r2.shared.util.logging.WarningLogger -import org.readium.r2.shared.util.pdf.PdfDocumentFactory import org.readium.r2.streamer.parser.PublicationParser -import org.readium.r2.streamer.parser.audio.AudioParser -import org.readium.r2.streamer.parser.epub.EpubParser -import org.readium.r2.streamer.parser.image.ImageParser -import org.readium.r2.streamer.parser.pdf.PdfParser -import org.readium.r2.streamer.parser.readium.ReadiumWebPubParser /** - * Opens a Publication using a list of parsers. + * Opens a [Publication] from an [Asset]. * - * The [PublicationOpener] is configured to use Readium's default parsers, which you can bypass - * using ignoreDefaultParsers. However, you can provide additional [parsers] which will take - * precedence over the default ones. This can also be used to provide an alternative configuration - * of a default parser. - * - * @param context Application context. - * @param parsers Parsers used to open a publication, in addition to the default parsers. - * @param ignoreDefaultParsers When true, only parsers provided in parsers will be used. + * @param parser Parses the content of a publication [Asset]. * @param contentProtections Opens DRM-protected publications. - * @param httpClient Service performing HTTP requests. - * @param pdfFactory Parses a PDF document, optionally protected by password. - * @param assetOpener Opens assets in case of indirection. * @param onCreatePublication Called on every parsed [Publication.Builder]. It can be used to modify * the manifest, the root container or the list of service factories of a [Publication]. */ -@OptIn(PdfSupport::class) public class PublicationOpener( - context: Context, - parsers: List = emptyList(), - ignoreDefaultParsers: Boolean = false, + private val parser: PublicationParser, contentProtections: List, - private val httpClient: HttpClient, - pdfFactory: PdfDocumentFactory<*>?, - assetOpener: AssetOpener, private val onCreatePublication: Publication.Builder.() -> Unit = {} ) { public sealed class OpenError( @@ -74,18 +48,6 @@ public class PublicationOpener( private val contentProtections: List = contentProtections + FallbackContentProtection() - private val defaultParsers: List = - listOfNotNull( - EpubParser(), - pdfFactory?.let { PdfParser(context, it) }, - ReadiumWebPubParser(context, httpClient, pdfFactory), - ImageParser(assetOpener.assetSniffer), - AudioParser(assetOpener.assetSniffer) - ) - - private val parsers: List = parsers + - if (!ignoreDefaultParsers) defaultParsers else emptyList() - /** * Opens a [Publication] from the given asset. * @@ -139,7 +101,7 @@ public class PublicationOpener( } } - val builder = parse(transformedAsset, warnings) + val builder = parser.parse(transformedAsset, warnings) .getOrElse { return Try.failure(wrapParserException(it)) } builder.apply { @@ -152,22 +114,6 @@ public class PublicationOpener( return Try.success(publication) } - private suspend fun parse( - publicationAsset: Asset, - warnings: WarningLogger? - ): Try { - for (parser in parsers) { - val result = parser.parse(publicationAsset, warnings) - if ( - result is Try.Success || - result is Try.Failure && result.value !is PublicationParser.ParseError.FormatNotSupported - ) { - return result - } - } - return Try.failure(PublicationParser.ParseError.FormatNotSupported()) - } - private fun wrapParserException(e: PublicationParser.ParseError): OpenError = when (e) { is PublicationParser.ParseError.FormatNotSupported -> diff --git a/readium/streamer/src/main/java/org/readium/r2/streamer/parser/PublicationParser.kt b/readium/streamer/src/main/java/org/readium/r2/streamer/parser/PublicationParser.kt index f7e6dd6eb6..64fb046586 100644 --- a/readium/streamer/src/main/java/org/readium/r2/streamer/parser/PublicationParser.kt +++ b/readium/streamer/src/main/java/org/readium/r2/streamer/parser/PublicationParser.kt @@ -6,13 +6,22 @@ package org.readium.r2.streamer.parser +import android.content.Context import org.readium.r2.shared.publication.Publication import org.readium.r2.shared.util.Try import org.readium.r2.shared.util.asset.Asset +import org.readium.r2.shared.util.asset.AssetOpener +import org.readium.r2.shared.util.http.HttpClient import org.readium.r2.shared.util.logging.WarningLogger +import org.readium.r2.shared.util.pdf.PdfDocumentFactory +import org.readium.r2.streamer.parser.audio.AudioParser +import org.readium.r2.streamer.parser.epub.EpubParser +import org.readium.r2.streamer.parser.image.ImageParser +import org.readium.r2.streamer.parser.pdf.PdfParser +import org.readium.r2.streamer.parser.readium.ReadiumWebPubParser /** - * Parses a Publication from an asset. + * Parses a [Publication] from an [Asset]. */ public interface PublicationParser { @@ -41,3 +50,56 @@ public interface PublicationParser { ParseError("An error occurred while trying to read asset.", cause) } } + +/** + * Default implementation of [PublicationParser] handling all the publication formats supported by + * Readium. + * + * @param parsers Parsers used to open a publication, in addition to the default parsers. They take precedence over the default ones. + * @param httpClient Service performing HTTP requests. + * @param pdfFactory Parses a PDF document, optionally protected by password. + * @param assetOpener Opens assets in case of indirection. + */ +public class DefaultPublicationParser( + context: Context, + parsers: List = emptyList(), + private val httpClient: HttpClient, + pdfFactory: PdfDocumentFactory<*>?, + assetOpener: AssetOpener +) : CompositePublicationParser( + parsers + listOfNotNull( + EpubParser(), + pdfFactory?.let { PdfParser(context, it) }, + ReadiumWebPubParser(context, httpClient, pdfFactory), + ImageParser(assetOpener.assetSniffer), + AudioParser(assetOpener.assetSniffer) + ) +) + +/** + * A composite [PublicationParser] which tries several parsers until it finds one which supports + * the asset. + */ +public open class CompositePublicationParser( + private val parsers: List +) : PublicationParser { + + public constructor(vararg parsers: PublicationParser) : + this(parsers.toList()) + + override suspend fun parse( + asset: Asset, + warnings: WarningLogger? + ): Try { + for (parser in parsers) { + val result = parser.parse(asset, warnings) + if ( + result is Try.Success || + result is Try.Failure && result.value !is PublicationParser.ParseError.FormatNotSupported + ) { + return result + } + } + return Try.failure(PublicationParser.ParseError.FormatNotSupported()) + } +} diff --git a/readium/streamer/src/main/java/org/readium/r2/streamer/parser/pdf/PdfParser.kt b/readium/streamer/src/main/java/org/readium/r2/streamer/parser/pdf/PdfParser.kt index e4a07a1a8b..1a4382dc2c 100644 --- a/readium/streamer/src/main/java/org/readium/r2/streamer/parser/pdf/PdfParser.kt +++ b/readium/streamer/src/main/java/org/readium/r2/streamer/parser/pdf/PdfParser.kt @@ -8,7 +8,6 @@ package org.readium.r2.streamer.parser.pdf import android.content.Context import org.readium.r2.shared.ExperimentalReadiumApi -import org.readium.r2.shared.PdfSupport import org.readium.r2.shared.publication.* import org.readium.r2.shared.publication.services.InMemoryCacheService import org.readium.r2.shared.publication.services.InMemoryCoverService @@ -27,7 +26,6 @@ import org.readium.r2.streamer.parser.PublicationParser /** * Parses a PDF file into a Readium [Publication]. */ -@PdfSupport @OptIn(ExperimentalReadiumApi::class) public class PdfParser( context: Context, diff --git a/test-app/src/main/java/org/readium/r2/testapp/Readium.kt b/test-app/src/main/java/org/readium/r2/testapp/Readium.kt index ec41d23a6d..0aefee6b64 100644 --- a/test-app/src/main/java/org/readium/r2/testapp/Readium.kt +++ b/test-app/src/main/java/org/readium/r2/testapp/Readium.kt @@ -25,6 +25,7 @@ import org.readium.r2.shared.util.http.HttpResourceFactory import org.readium.r2.shared.util.resource.CompositeResourceFactory import org.readium.r2.shared.util.zip.ZipArchiveOpener import org.readium.r2.streamer.PublicationOpener +import org.readium.r2.streamer.parser.DefaultPublicationParser /** * Holds the shared Readium objects and services used by the app. @@ -70,15 +71,17 @@ class Readium(context: Context) { ) /** - * The PublicationFactory is used to parse and open publications. + * The PublicationFactory is used to open publications. */ val publicationOpener = PublicationOpener( - context, - contentProtections = contentProtections, - assetOpener = assetOpener, - httpClient = httpClient, - // Only required if you want to support PDF files using the PDFium adapter. - pdfFactory = PdfiumDocumentFactory(context) + parser = DefaultPublicationParser( + context, + assetOpener = assetOpener, + httpClient = httpClient, + // Only required if you want to support PDF files using the PDFium adapter. + pdfFactory = PdfiumDocumentFactory(context) + ), + contentProtections = contentProtections ) fun onLcpDialogAuthenticationParentAttached(view: View) { From 6efb3cce5cf2b8073132e84f166cb29a1198587c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Tue, 9 Jan 2024 12:37:18 +0100 Subject: [PATCH 2/2] Minor change --- .../readium/r2/streamer/parser/PublicationParser.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readium/streamer/src/main/java/org/readium/r2/streamer/parser/PublicationParser.kt b/readium/streamer/src/main/java/org/readium/r2/streamer/parser/PublicationParser.kt index 64fb046586..3c2b007a60 100644 --- a/readium/streamer/src/main/java/org/readium/r2/streamer/parser/PublicationParser.kt +++ b/readium/streamer/src/main/java/org/readium/r2/streamer/parser/PublicationParser.kt @@ -55,19 +55,19 @@ public interface PublicationParser { * Default implementation of [PublicationParser] handling all the publication formats supported by * Readium. * - * @param parsers Parsers used to open a publication, in addition to the default parsers. They take precedence over the default ones. + * @param additionalParsers Parsers used to open a publication, in addition to the default parsers. They take precedence over the default ones. * @param httpClient Service performing HTTP requests. * @param pdfFactory Parses a PDF document, optionally protected by password. * @param assetOpener Opens assets in case of indirection. */ public class DefaultPublicationParser( context: Context, - parsers: List = emptyList(), + additionalParsers: List = emptyList(), private val httpClient: HttpClient, pdfFactory: PdfDocumentFactory<*>?, assetOpener: AssetOpener -) : CompositePublicationParser( - parsers + listOfNotNull( +) : PublicationParser by CompositePublicationParser( + additionalParsers + listOfNotNull( EpubParser(), pdfFactory?.let { PdfParser(context, it) }, ReadiumWebPubParser(context, httpClient, pdfFactory), @@ -80,7 +80,7 @@ public class DefaultPublicationParser( * A composite [PublicationParser] which tries several parsers until it finds one which supports * the asset. */ -public open class CompositePublicationParser( +public class CompositePublicationParser( private val parsers: List ) : PublicationParser {