Skip to content

Commit

Permalink
Refactor errors (readium#422)
Browse files Browse the repository at this point in the history
  • Loading branch information
qnga committed Dec 11, 2023
1 parent 6941ee9 commit c9a09ac
Show file tree
Hide file tree
Showing 251 changed files with 6,571 additions and 6,405 deletions.
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

0 comments on commit c9a09ac

Please sign in to comment.