Skip to content
Merged
Show file tree
Hide file tree
Changes from 70 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
10fe08e
Revisit factories
qnga Oct 24, 2023
8fa923c
WIP
qnga Oct 24, 2023
8244e92
WIP
qnga Oct 26, 2023
0d433c4
WIP
qnga Oct 29, 2023
d35bd5c
WIP
qnga Oct 31, 2023
dec440a
Refactor ArchiveFactory
qnga Nov 3, 2023
9b80755
Refactor container and resource
qnga Nov 6, 2023
524d9cc
Various
qnga Nov 15, 2023
356cf8a
Introduce HttpStatus
qnga Nov 16, 2023
14b6f48
Remove entries
qnga Nov 16, 2023
06d64f7
Get rid of Other errors
qnga Nov 17, 2023
88ffc01
Update error handling in testapp
qnga Nov 17, 2023
dbd73a5
Lint
qnga Nov 17, 2023
2ce91e0
Refactor LcpException and move UserError to test-app
qnga Nov 19, 2023
6d5b5a3
Various changes
qnga Nov 20, 2023
69d9dd9
Fix uses of Try.assertSuccess
qnga Nov 20, 2023
e720ebc
Fix PsPDFKit errors
qnga Nov 20, 2023
7053cd3
Small fixes
qnga Nov 20, 2023
e25514d
Small fixes
qnga Nov 20, 2023
d89a6dd
Fix sniffing and AssetRetriever
qnga Nov 20, 2023
b683e06
Fix SingleResourceContainer
qnga Nov 20, 2023
c99001e
Optimize ZIP sniffing
qnga Nov 20, 2023
5257109
Remove ReadError.Other
qnga Nov 21, 2023
7d55985
Add media type and filename properties on Resource
qnga Nov 21, 2023
3c77ec1
Fix
qnga Nov 21, 2023
6b200b1
Remove MediaTypeRetriever from EpubParser
qnga Nov 23, 2023
efb5671
Clarify and move MediaTypeRetriever
qnga Nov 23, 2023
1d85516
Refactor ArchiveFactory
qnga Nov 23, 2023
2df5555
Introduce BlobMediaTypeRetriever
qnga Nov 23, 2023
d92f49d
Remove passwords from ArchiveFactory
qnga Nov 27, 2023
6b3688c
Cosmetic change
qnga Nov 27, 2023
58a9846
Remove Resource.mediaType()
qnga Nov 27, 2023
1b167a4
Fix most of the tests
qnga Nov 28, 2023
268c3f5
Cosmetic changes
qnga Nov 28, 2023
8ec36a8
Cosmetic changes
qnga Nov 29, 2023
026c4d7
Merge branch 'v3' into refactor-errors
qnga Nov 29, 2023
7b677d8
Various fixes
qnga Nov 29, 2023
4b6b77f
Renaming
qnga Nov 29, 2023
881772e
Naming
qnga Nov 29, 2023
41ebe8f
Moves
qnga Nov 29, 2023
faea60c
Cosmetic changes
qnga Nov 29, 2023
c0d3f86
Cosmetic changes
qnga Nov 29, 2023
8f4002f
Small fixes
qnga Nov 29, 2023
fd211ad
Remove FLAG_SECURE
mickael-menu Nov 30, 2023
1d7f896
Fix ReadableInputStream lifecycle issues
qnga Nov 30, 2023
57d5183
Rename OverflowableNavigator
qnga Nov 30, 2023
6755380
Cosmetic changes
qnga Nov 30, 2023
1b05cea
Move media type retriever back to `mediatype` package
mickael-menu Dec 1, 2023
61db1b1
Various fixes and cosmetic changes
qnga Dec 5, 2023
cb567c9
Minor changes
qnga Dec 6, 2023
16eca61
Fix SearchService errors
qnga Dec 6, 2023
2adb5c8
Fix ExoMediaPlayer
qnga Dec 6, 2023
101017f
Refactor BufferingResource
qnga Dec 6, 2023
b25739b
Cosmetic changes
qnga Dec 6, 2023
296c722
Fix LcpFallbackContentProtection
qnga Dec 6, 2023
10a1f5d
Improve MediaTypeRetriever hint sniffing efficiency
qnga Dec 6, 2023
678238b
Cosmetic changes
qnga Dec 6, 2023
77f11a7
Reorganization
qnga Dec 6, 2023
db48f8d
Doc
qnga Dec 6, 2023
632c8ca
Cosmetic changes
qnga Dec 6, 2023
fce3d33
Cosmetic changes
qnga Dec 6, 2023
ec16df2
Refactor decoding
qnga Dec 7, 2023
14a1456
Clean up decoding
qnga Dec 8, 2023
565df38
Cosmetic changes
qnga Dec 8, 2023
379bac0
Refactor user errors in testapp
qnga Dec 8, 2023
cc19717
Fix DownloadManager errors
qnga Dec 8, 2023
7100890
Small fix
qnga Dec 8, 2023
ce4dbd5
Small fix
qnga Dec 8, 2023
f65b677
Various changes
qnga Dec 8, 2023
c8e0066
Minor changes
mickael-menu Dec 10, 2023
e225659
Keep `HttpRequest.Builder.url` immutable
mickael-menu Dec 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ captures/
.idea/libraries
.idea/jarRepositories.xml
.idea/misc.xml
.idea/migrations.xml
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import androidx.media3.datasource.TransferListener
import java.io.IOException
import kotlinx.coroutines.runBlocking
import org.readium.r2.shared.publication.Publication
import org.readium.r2.shared.util.data.ReadException
import org.readium.r2.shared.util.getOrThrow
import org.readium.r2.shared.util.resource.Resource
import org.readium.r2.shared.util.resource.buffered
Expand Down Expand Up @@ -63,16 +64,15 @@ internal class ExoPlayerDataSource internal constructor(
private var openedResource: OpenedResource? = null

override fun open(dataSpec: DataSpec): Long {
val link = dataSpec.uri.toUrl()
val resource = dataSpec.uri.toUrl()
?.let { publication.linkWithHref(it) }
?.let { publication.get(it) }
// Significantly improves performances, in particular with deflated ZIP entries.
?.buffered(resourceLength = cachedLengths[dataSpec.uri.toString()])
?: throw ExoPlayerDataSourceException.NotFound(
"Can't find a [Link] for URI: ${dataSpec.uri}. Make sure you only request resources declared in the manifest."
)

val resource = publication.get(link)
// Significantly improves performances, in particular with deflated ZIP entries.
.buffered(resourceLength = cachedLengths[dataSpec.uri.toString()])

openedResource = OpenedResource(
resource = resource,
uri = dataSpec.uri,
Expand Down Expand Up @@ -117,6 +117,7 @@ internal class ExoPlayerDataSource internal constructor(
val data = runBlocking {
openedResource.resource
.read(range = openedResource.position until (openedResource.position + length))
.mapFailure { ReadException(it) }
.getOrThrow()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import org.readium.r2.shared.ExperimentalReadiumApi
import org.readium.r2.shared.publication.Locator
import org.readium.r2.shared.publication.Publication
import org.readium.r2.shared.publication.indexOfFirstWithHref
import org.readium.r2.shared.util.Try

/**
* Main component to use the audio navigator with the ExoPlayer adapter.
Expand All @@ -38,7 +39,7 @@ public class ExoPlayerEngineProvider(
publication: Publication,
initialLocator: Locator,
initialPreferences: ExoPlayerPreferences
): ExoPlayerEngine {
): Try<ExoPlayerEngine, Nothing> {
val metadataFactory = metadataProvider.createMetadataFactory(publication)
val settingsResolver = ExoPlayerSettingsResolver(defaults)
val dataSourceFactory: DataSource.Factory = ExoPlayerDataSource.Factory(publication)
Expand All @@ -56,7 +57,7 @@ public class ExoPlayerEngineProvider(
}
)

return ExoPlayerEngine(
val engine = ExoPlayerEngine(
application = application,
settingsResolver = settingsResolver,
playlist = playlist,
Expand All @@ -66,6 +67,8 @@ public class ExoPlayerEngineProvider(
initialPosition = initialPosition,
initialPreferences = initialPreferences
)

return Try.success(engine)
}

override fun createPreferenceEditor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ import org.readium.r2.shared.InternalReadiumApi
import org.readium.r2.shared.extensions.md5
import org.readium.r2.shared.extensions.tryOrNull
import org.readium.r2.shared.util.Try
import org.readium.r2.shared.util.data.ReadError
import org.readium.r2.shared.util.data.ReadTry
import org.readium.r2.shared.util.flatMap
import org.readium.r2.shared.util.pdf.PdfDocument
import org.readium.r2.shared.util.pdf.PdfDocumentFactory
import org.readium.r2.shared.util.resource.Resource
import org.readium.r2.shared.util.resource.ResourceTry
import org.readium.r2.shared.util.resource.mapCatching
import org.readium.r2.shared.util.use
import timber.log.Timber

Expand Down Expand Up @@ -86,25 +87,34 @@ public class PdfiumDocumentFactory(context: Context) : PdfDocumentFactory<Pdfium

private val core by lazy { PdfiumCore(context.applicationContext) }

override suspend fun open(resource: Resource, password: String?): ResourceTry<PdfiumDocument> {
override suspend fun open(resource: Resource, password: String?): ReadTry<PdfiumDocument> {
// First try to open the resource as a file on the FS for performance improvement, as
// PDFium requires the whole PDF document to be loaded in memory when using raw bytes.
return resource.openAsFile(password)
?: resource.openBytes(password)
}

private suspend fun Resource.openAsFile(password: String?): ResourceTry<PdfiumDocument>? =
private suspend fun Resource.openAsFile(password: String?): ReadTry<PdfiumDocument>? =
tryOrNull {
source?.toFile()?.let { file ->
sourceUrl?.toFile()?.let { file ->
withContext(Dispatchers.IO) {
Try.success(core.fromFile(file, password))
}
}
}

private suspend fun Resource.openBytes(password: String?): ResourceTry<PdfiumDocument> =
private suspend fun Resource.openBytes(password: String?): ReadTry<PdfiumDocument> =
use {
read().mapCatching { core.fromBytes(it, password) }
it.read()
.flatMap { bytes ->
try {
Try.success(
core.fromBytes(bytes, password)
)
} catch (e: Exception) {
Try.failure(ReadError.Decoding(e))
}
}
}

private fun PdfiumCore.fromFile(file: File, password: String?): PdfiumDocument =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ import org.readium.r2.shared.ExperimentalReadiumApi
import org.readium.r2.shared.publication.Publication
import org.readium.r2.shared.util.SingleJob
import org.readium.r2.shared.util.Url
import org.readium.r2.shared.util.data.ReadError
import org.readium.r2.shared.util.getOrElse
import org.readium.r2.shared.util.resource.Resource
import org.readium.r2.shared.util.toDebugDescription
import timber.log.Timber

@ExperimentalReadiumApi
Expand All @@ -40,7 +41,7 @@ public class PdfiumDocumentFragment internal constructor(
) : PdfDocumentFragment<PdfiumSettings>() {

internal interface Listener {
fun onResourceLoadFailed(href: Url, error: Resource.Exception)
fun onResourceLoadFailed(href: Url, error: ReadError)
fun onConfigurePdfView(configurator: PDFView.Configurator)
fun onTap(point: PointF): Boolean
}
Expand Down Expand Up @@ -69,12 +70,13 @@ public class PdfiumDocumentFragment internal constructor(
val context = context?.applicationContext ?: return

resetJob.launch {
val resource = requireNotNull(publication.get(href))
val document = PdfiumDocumentFactory(context)
// PDFium crashes when reusing the same PdfDocument, so we must not cache it.
// .cachedIn(publication)
.open(publication.get(href), null)
.open(resource, null)
.getOrElse { error ->
Timber.e(error)
Timber.e(error.toDebugDescription())
listener?.onResourceLoadFailed(href, error)
return@launch
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ package org.readium.adapter.pdfium.navigator

import android.graphics.PointF
import com.github.barteksc.pdfviewer.PDFView
import org.readium.r2.navigator.OverflowNavigator
import org.readium.r2.navigator.SimplePresentation
import org.readium.r2.navigator.OverflowableNavigator
import org.readium.r2.navigator.SimpleOverflow
import org.readium.r2.navigator.input.TapEvent
import org.readium.r2.navigator.pdf.PdfDocumentFragmentInput
import org.readium.r2.navigator.pdf.PdfEngineProvider
Expand All @@ -19,7 +19,7 @@ import org.readium.r2.shared.ExperimentalReadiumApi
import org.readium.r2.shared.publication.Metadata
import org.readium.r2.shared.publication.Publication
import org.readium.r2.shared.util.Url
import org.readium.r2.shared.util.resource.Resource
import org.readium.r2.shared.util.data.ReadError

/**
* Main component to use the PDF navigator with the PDFium adapter.
Expand Down Expand Up @@ -49,7 +49,7 @@ public class PdfiumEngineProvider(
initialPageIndex = input.pageIndex,
initialSettings = input.settings,
listener = object : PdfiumDocumentFragment.Listener {
override fun onResourceLoadFailed(href: Url, error: Resource.Exception) {
override fun onResourceLoadFailed(href: Url, error: ReadError) {
input.navigatorListener?.onResourceLoadFailed(href, error)
}

Expand All @@ -68,8 +68,8 @@ public class PdfiumEngineProvider(
return settingsPolicy.settings(preferences)
}

override fun computePresentation(settings: PdfiumSettings): OverflowNavigator.Presentation =
SimplePresentation(
override fun computeOverflow(settings: PdfiumSettings): OverflowableNavigator.Overflow =
SimpleOverflow(
readingProgression = settings.readingProgression,
scroll = true,
axis = settings.scrollAxis
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,39 @@ import com.pspdfkit.document.PageBinding
import com.pspdfkit.document.PdfDocument as _PsPdfKitDocument
import com.pspdfkit.document.PdfDocumentLoader
import com.pspdfkit.exceptions.InvalidPasswordException
import com.pspdfkit.exceptions.InvalidSignatureException
import java.io.IOException
import kotlin.reflect.KClass
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.readium.r2.shared.publication.ReadingProgression
import org.readium.r2.shared.util.ThrowableError
import org.readium.r2.shared.util.Try
import org.readium.r2.shared.util.data.ReadError
import org.readium.r2.shared.util.data.ReadTry
import org.readium.r2.shared.util.pdf.PdfDocument
import org.readium.r2.shared.util.pdf.PdfDocumentFactory
import org.readium.r2.shared.util.resource.Resource
import org.readium.r2.shared.util.resource.ResourceTry
import timber.log.Timber

public class PsPdfKitDocumentFactory(context: Context) : PdfDocumentFactory<PsPdfKitDocument> {
private val context = context.applicationContext

override val documentType: KClass<PsPdfKitDocument> = PsPdfKitDocument::class

override suspend fun open(resource: Resource, password: String?): ResourceTry<PsPdfKitDocument> =
open(context, DocumentSource(ResourceDataProvider(resource), password))

private suspend fun open(context: Context, documentSource: DocumentSource): ResourceTry<PsPdfKitDocument> =
override suspend fun open(resource: Resource, password: String?): ReadTry<PsPdfKitDocument> =
withContext(Dispatchers.IO) {
val dataProvider = ResourceDataProvider(resource)
val documentSource = DocumentSource(dataProvider, password)
try {
Try.success(
PsPdfKitDocument(PdfDocumentLoader.openDocument(context, documentSource))
)
val innerDocument = PdfDocumentLoader.openDocument(context, documentSource)
Try.success(PsPdfKitDocument(innerDocument))
} catch (e: InvalidPasswordException) {
Try.failure(Resource.Exception.Forbidden(e))
} catch (e: CancellationException) {
throw e
} catch (e: Throwable) {
Try.failure(Resource.Exception.wrap(e))
Try.failure(ReadError.Decoding(ThrowableError(e)))
} catch (e: InvalidSignatureException) {
Try.failure(ReadError.Decoding(ThrowableError(e)))
} catch (e: IOException) {
Try.failure(dataProvider.error!!)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,36 @@
package org.readium.adapter.pspdfkit.document

import com.pspdfkit.document.providers.DataProvider
import java.util.*
import java.util.UUID
import kotlinx.coroutines.runBlocking
import org.readium.r2.shared.util.data.ReadError
import org.readium.r2.shared.util.getOrElse
import org.readium.r2.shared.util.isLazyInitialized
import org.readium.r2.shared.util.resource.Resource
import org.readium.r2.shared.util.resource.synchronized
import org.readium.r2.shared.util.toDebugDescription
import timber.log.Timber

internal class ResourceDataProvider(
resource: Resource,
private val onResourceError: (Resource.Exception) -> Unit = { Timber.e(it) }
private val onResourceError: (ReadError) -> Unit = { Timber.e(it.toDebugDescription()) }
) : DataProvider {

var error: ReadError? = null

private val resource =
// PSPDFKit accesses the resource from multiple threads.
resource.synchronized()

private val length: Long = runBlocking {
resource.length()
.getOrElse {
onResourceError(it)
DataProvider.FILE_SIZE_UNKNOWN.toLong()
}
private val length by lazy {
runBlocking {
resource.length()
.getOrElse {
error = it
onResourceError(it)
DataProvider.FILE_SIZE_UNKNOWN.toLong()
}
}
}

override fun getSize(): Long = length
Expand All @@ -47,13 +54,15 @@ internal class ResourceDataProvider(
val range = offset until (offset + size)
resource.read(range)
.getOrElse {
error = it
onResourceError(it)
DataProvider.NO_DATA_AVAILABLE
}
}

override fun release() {
if (::resource.isLazyInitialized) {
error = null
runBlocking { resource.close() }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ import org.readium.r2.shared.ExperimentalReadiumApi
import org.readium.r2.shared.publication.Publication
import org.readium.r2.shared.publication.services.isProtected
import org.readium.r2.shared.util.Url
import org.readium.r2.shared.util.data.ReadError
import org.readium.r2.shared.util.data.ReadTry
import org.readium.r2.shared.util.pdf.cachedIn
import org.readium.r2.shared.util.resource.Resource
import org.readium.r2.shared.util.resource.ResourceTry
import timber.log.Timber

@ExperimentalReadiumApi
Expand All @@ -69,7 +69,7 @@ public class PsPdfKitDocumentFragment internal constructor(
) : PdfDocumentFragment<PsPdfKitSettings>() {

internal interface Listener {
fun onResourceLoadFailed(href: Url, error: Resource.Exception)
fun onResourceLoadFailed(href: Url, error: ReadError)
fun onConfigurePdfView(builder: PdfConfiguration.Builder): PdfConfiguration.Builder
fun onTap(point: PointF): Boolean
}
Expand All @@ -90,13 +90,13 @@ public class PsPdfKitDocumentFragment internal constructor(
private val psPdfKitListener = PsPdfKitListener()

private class DocumentViewModel(
document: suspend () -> ResourceTry<PsPdfKitDocument>
document: suspend () -> ReadTry<PsPdfKitDocument>
) : ViewModel() {

private val _document: Deferred<ResourceTry<PsPdfKitDocument>> =
private val _document: Deferred<ReadTry<PsPdfKitDocument>> =
viewModelScope.async { document() }

suspend fun loadDocument(): ResourceTry<PsPdfKitDocument> =
suspend fun loadDocument(): ReadTry<PsPdfKitDocument> =
_document.await()

@OptIn(ExperimentalCoroutinesApi::class)
Expand All @@ -114,9 +114,10 @@ public class PsPdfKitDocumentFragment internal constructor(
createViewModelFactory {
DocumentViewModel(
document = {
val resource = requireNotNull(publication.get(href))
PsPdfKitDocumentFactory(requireContext())
.cachedIn(publication)
.open(publication.get(href), null)
.open(resource, null)
}
)
}
Expand Down
Loading