diff --git a/README-CHANGES.xml b/README-CHANGES.xml index 8ec58304a..18a64ddd7 100644 --- a/README-CHANGES.xml +++ b/README-CHANGES.xml @@ -370,7 +370,7 @@ - + @@ -400,13 +400,18 @@ - + + + + + + diff --git a/org.thepalaceproject.android.platform b/org.thepalaceproject.android.platform index 22fb9049e..ce68d1001 160000 --- a/org.thepalaceproject.android.platform +++ b/org.thepalaceproject.android.platform @@ -1 +1 @@ -Subproject commit 22fb9049e346c52a5f641fb54a196e8dac7f1da0 +Subproject commit ce68d100180a4fbcee5b76e2e2dc7eea43ccb856 diff --git a/simplified-app-palace/build.gradle.kts b/simplified-app-palace/build.gradle.kts index 93a58388b..2efb676be 100644 --- a/simplified-app-palace/build.gradle.kts +++ b/simplified-app-palace/build.gradle.kts @@ -531,8 +531,6 @@ dependencies { implementation(libs.kotlinx.datetime) implementation(libs.logback.android) implementation(libs.moznion.uribuildertiny) - implementation(libs.nano.httpd) - implementation(libs.nano.httpd.nanolets) implementation(libs.nypl.readium) implementation(libs.okhttp3) implementation(libs.okio) diff --git a/simplified-tests/build.gradle.kts b/simplified-tests/build.gradle.kts index 312a7ff22..e990a117f 100644 --- a/simplified-tests/build.gradle.kts +++ b/simplified-tests/build.gradle.kts @@ -204,8 +204,6 @@ val dependencyObjects = listOf( libs.mockito.android, libs.mockito.core, libs.mockito.kotlin, - libs.nano.httpd, - libs.nano.httpd.nanolets, libs.nypl.readium, libs.objenesis, libs.okhttp3, diff --git a/simplified-viewer-pdf-pdfjs/build.gradle.kts b/simplified-viewer-pdf-pdfjs/build.gradle.kts index 4fa91443d..4cceae2ce 100644 --- a/simplified-viewer-pdf-pdfjs/build.gradle.kts +++ b/simplified-viewer-pdf-pdfjs/build.gradle.kts @@ -54,8 +54,6 @@ dependencies { implementation(libs.kotlin.reflect) implementation(libs.kotlin.stdlib) implementation(libs.kotlinx.coroutines) - implementation(libs.nano.httpd) - implementation(libs.nano.httpd.nanolets) implementation(libs.palace.drm.core) implementation(libs.palace.theme) implementation(libs.pdfium.android) diff --git a/simplified-viewer-pdf-pdfjs/src/main/java/org/librarysimplified/viewer/pdf/pdfjs/PdfServer.kt b/simplified-viewer-pdf-pdfjs/src/main/java/org/librarysimplified/viewer/pdf/pdfjs/PdfServer.kt index d8b24cc77..8ad042671 100644 --- a/simplified-viewer-pdf-pdfjs/src/main/java/org/librarysimplified/viewer/pdf/pdfjs/PdfServer.kt +++ b/simplified-viewer-pdf-pdfjs/src/main/java/org/librarysimplified/viewer/pdf/pdfjs/PdfServer.kt @@ -3,6 +3,7 @@ package org.librarysimplified.viewer.pdf.pdfjs import android.content.Context import android.net.Uri import kotlinx.coroutines.runBlocking +import org.librarysimplified.viewer.pdf.pdfjs.factory.PdfDocumentFactory import org.nanohttpd.protocols.http.IHTTPSession import org.nanohttpd.protocols.http.response.Response import org.nanohttpd.protocols.http.response.Status @@ -53,6 +54,7 @@ class PdfServer private constructor( ), PdfParser( context = context, + pdfFactory = PdfDocumentFactory(context) ) ), contentProtections = contentProtections, diff --git a/simplified-viewer-pdf-pdfjs/src/main/java/org/librarysimplified/viewer/pdf/pdfjs/factory/PdfDocumentFactory.kt b/simplified-viewer-pdf-pdfjs/src/main/java/org/librarysimplified/viewer/pdf/pdfjs/factory/PdfDocumentFactory.kt new file mode 100644 index 000000000..db1013c3c --- /dev/null +++ b/simplified-viewer-pdf-pdfjs/src/main/java/org/librarysimplified/viewer/pdf/pdfjs/factory/PdfDocumentFactory.kt @@ -0,0 +1,70 @@ +package org.librarysimplified.viewer.pdf.pdfjs.factory + +import android.content.Context +import android.os.ParcelFileDescriptor +import com.shockwave.pdfium.PdfDocument +import com.shockwave.pdfium.PdfiumCore +import org.readium.r2.shared.PdfSupport +import org.readium.r2.shared.extensions.md5 +import org.readium.r2.shared.extensions.tryOrNull +import org.readium.r2.shared.fetcher.Resource +import org.readium.r2.shared.util.pdf.PdfDocumentFactory +import org.readium.r2.shared.util.use +import java.io.File +import kotlin.reflect.KClass + +@OptIn(PdfSupport::class) +class PdfDocumentFactory(context: Context) : PdfDocumentFactory { + override val documentType: KClass = PdfReaderDocument::class + + private val core by lazy { PdfiumCore(context.applicationContext) } + + override suspend fun open(file: File, password: String?): PdfReaderDocument { + return core.fromFile(file, password) + } + + override suspend fun open(resource: Resource, password: String?): PdfReaderDocument { + return resource.openAsFile(password) ?: resource.openBytes(password) + } + + private suspend fun Resource.openAsFile(password: String?): PdfReaderDocument? { + return file?.let { + tryOrNull { open(it, password) } + } + } + + private suspend fun Resource.openBytes(password: String?): PdfReaderDocument { + return use { + core.fromBytes(read().getOrThrow(), password) + } + } + + private fun PdfiumCore.fromFile(file: File, password: String?): PdfReaderDocument { + return fromDocument( + newDocument(ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY), password), + identifier = file.md5() + ) + } + + /** + * Creates a [PdfReaderDocument] from raw bytes. + */ + private fun PdfiumCore.fromBytes(bytes: ByteArray, password: String?): PdfReaderDocument { + return fromDocument( + newDocument(bytes, password), + identifier = bytes.md5() + ) + } + + private fun PdfiumCore.fromDocument( + document: PdfDocument, + identifier: String? + ): PdfReaderDocument { + return PdfReaderDocument( + core = this, + document = document, + identifier = identifier, + pageCount = getPageCount(document) + ) + } +} diff --git a/simplified-viewer-pdf-pdfjs/src/main/java/org/librarysimplified/viewer/pdf/pdfjs/factory/PdfReaderDocument.kt b/simplified-viewer-pdf-pdfjs/src/main/java/org/librarysimplified/viewer/pdf/pdfjs/factory/PdfReaderDocument.kt new file mode 100644 index 000000000..ead15294c --- /dev/null +++ b/simplified-viewer-pdf-pdfjs/src/main/java/org/librarysimplified/viewer/pdf/pdfjs/factory/PdfReaderDocument.kt @@ -0,0 +1,73 @@ +package org.librarysimplified.viewer.pdf.pdfjs.factory + +import android.content.Context +import android.graphics.Bitmap +import com.shockwave.pdfium.PdfiumCore +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.readium.r2.shared.PdfSupport +import org.readium.r2.shared.util.pdf.PdfDocument +import org.slf4j.LoggerFactory +import com.shockwave.pdfium.PdfDocument as PdfiumDocument + +class PdfReaderDocument( + val core: PdfiumCore, + val document: PdfiumDocument, + override val identifier: String?, + override val pageCount: Int +) : PdfDocument { + + override val title: String? + get() = metadata.title + + override val author: String? + get() = metadata.author + + override val subject: String? + get() = metadata.subject + + override val keywords: List + get() = metadata.keywords + .split(",") + .map { it.trim() } + .filter { it.isNotEmpty() } + + private val logger = LoggerFactory.getLogger(PdfReaderDocument::class.java) + private val metadata: PdfiumDocument.Meta by lazy { core.getDocumentMeta(document) } + + override suspend fun cover(context: Context): Bitmap? { + return withContext(Dispatchers.IO) { + try { + core.openPage(document, 0) + val width = core.getPageWidth(document, 0) + val height = core.getPageHeight(document, 0) + val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + core.renderPageBitmap(document, bitmap, 0, 0, 0, width, height, false) + bitmap + } catch (e: Exception) { + logger.error("Error rendering page: ", e) + null + } catch (e: OutOfMemoryError) { + logger.error("Error rendering page: ", e) + null + } + } + } + + override val outline: List by lazy { + core.getTableOfContents(document).map { it.toOutlineNode() } + } + + override suspend fun close() { + // do nothing + } + + @OptIn(PdfSupport::class) + private fun PdfiumDocument.Bookmark.toOutlineNode(): PdfDocument.OutlineNode { + return PdfDocument.OutlineNode( + title = title, + pageNumber = pageIdx.toInt() + 1, + children = children.map { it.toOutlineNode() }, + ) + } +}