ImageRepository: make jpegBytes and getThumbnail suspend
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
package org.fairscan.app.data
|
||||
|
||||
import org.fairscan.app.domain.JpegProvider
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.OutputStream
|
||||
@@ -25,7 +26,7 @@ data class GeneratedPdf(
|
||||
)
|
||||
|
||||
fun interface PdfWriter {
|
||||
fun writePdfFromJpegs(jpegs: Sequence<ByteArray>, outputStream: OutputStream): Int
|
||||
suspend fun writePdfFromJpegs(jpegs: List<JpegProvider>, outputStream: OutputStream): Int
|
||||
}
|
||||
|
||||
class FileManager(
|
||||
@@ -42,7 +43,7 @@ class FileManager(
|
||||
}
|
||||
}
|
||||
|
||||
fun generatePdf(jpegs: Sequence<ByteArray>): GeneratedPdf {
|
||||
suspend fun generatePdf(jpegs: List<JpegProvider>): GeneratedPdf {
|
||||
pdfDir.mkdirs()
|
||||
require(pdfDir.exists() && pdfDir.isDirectory) { "Invalid pdfDir: $pdfDir" }
|
||||
val file = File(pdfDir, "${System.currentTimeMillis()}.pdf")
|
||||
|
||||
@@ -170,13 +170,12 @@ class ImageRepository(
|
||||
saveMetadata()
|
||||
}
|
||||
|
||||
fun jpegBytes(key: PageViewKey): ByteArray? = runBlocking(Dispatchers.IO) {
|
||||
suspend fun jpegBytes(key: PageViewKey): ByteArray? =
|
||||
getOrCompute(imageCache, key, ::computeProcessedImage)
|
||||
}
|
||||
|
||||
fun getThumbnail(key: PageViewKey): ByteArray? = runBlocking(Dispatchers.IO) {
|
||||
|
||||
suspend fun getThumbnail(key: PageViewKey): ByteArray? =
|
||||
getOrCompute(thumbnailCache, key, ::computeThumbnail)
|
||||
}
|
||||
|
||||
// --- Cache compute functions ---
|
||||
|
||||
|
||||
@@ -22,36 +22,46 @@ import org.fairscan.imageprocessing.resizeForMaxPixels
|
||||
import org.fairscan.imageprocessing.scaledTo
|
||||
import org.opencv.core.Mat
|
||||
|
||||
fun interface JpegProvider {
|
||||
suspend fun get(): ByteArray
|
||||
}
|
||||
|
||||
suspend fun jpegsForExport(
|
||||
imageRepository: ImageRepository,
|
||||
exportQuality: ExportQuality
|
||||
): Sequence<ByteArray> {
|
||||
): List<JpegProvider> {
|
||||
|
||||
val pages = imageRepository.pages().asSequence()
|
||||
val pages = imageRepository.pages()
|
||||
return when (exportQuality) {
|
||||
ExportQuality.BALANCED -> pages.map { jpegBytes(it, imageRepository) }
|
||||
ExportQuality.BALANCED -> pages.map {
|
||||
JpegProvider { jpegBytes(it, imageRepository) }
|
||||
}
|
||||
|
||||
ExportQuality.LOW -> pages.map { page ->
|
||||
resizeJpegBytesForMaxPixels(
|
||||
jpegBytes = jpegBytes(page, imageRepository),
|
||||
maxPixels = exportQuality.maxPixels.toDouble(),
|
||||
jpegQuality = exportQuality.jpegQuality
|
||||
)
|
||||
JpegProvider {
|
||||
resizeJpegBytesForMaxPixels(
|
||||
jpegBytes = jpegBytes(page, imageRepository),
|
||||
maxPixels = exportQuality.maxPixels.toDouble(),
|
||||
jpegQuality = exportQuality.jpegQuality
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ExportQuality.HIGH -> pages.map { page ->
|
||||
val sourceJpegBytes = imageRepository.sourceJpegBytes(page.id)
|
||||
val pageMetadata = page.metadata
|
||||
val manualRotation = page.manualRotation
|
||||
if (sourceJpegBytes != null && pageMetadata != null)
|
||||
prepareJpegForHigh(sourceJpegBytes, pageMetadata, manualRotation, exportQuality)
|
||||
else
|
||||
jpegBytes(page, imageRepository)
|
||||
JpegProvider {
|
||||
val sourceJpegBytes = imageRepository.sourceJpegBytes(page.id)
|
||||
val pageMetadata = page.metadata
|
||||
val manualRotation = page.manualRotation
|
||||
if (sourceJpegBytes != null && pageMetadata != null)
|
||||
prepareJpegForHigh(sourceJpegBytes, pageMetadata, manualRotation, exportQuality)
|
||||
else
|
||||
jpegBytes(page, imageRepository)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun jpegBytes(page: ScanPage, imageRepository: ImageRepository): ByteArray {
|
||||
private suspend fun jpegBytes(page: ScanPage, imageRepository: ImageRepository): ByteArray {
|
||||
val key = page.key()
|
||||
return imageRepository.jpegBytes(key)
|
||||
?: throw IllegalArgumentException("JPEG not found for $key")
|
||||
|
||||
@@ -22,17 +22,18 @@ import com.tom_roush.pdfbox.pdmodel.common.PDRectangle
|
||||
import com.tom_roush.pdfbox.pdmodel.graphics.image.JPEGFactory
|
||||
import org.fairscan.app.BuildConfig
|
||||
import org.fairscan.app.data.PdfWriter
|
||||
import org.fairscan.app.domain.JpegProvider
|
||||
import java.io.OutputStream
|
||||
import java.util.Calendar
|
||||
|
||||
class AndroidPdfWriter : PdfWriter {
|
||||
override fun writePdfFromJpegs(jpegs: Sequence<ByteArray>, outputStream: OutputStream): Int {
|
||||
override suspend fun writePdfFromJpegs(jpegs: List<JpegProvider>, outputStream: OutputStream): Int {
|
||||
val doc = PDDocument()
|
||||
doc.documentInformation.creationDate = Calendar.getInstance()
|
||||
doc.documentInformation.creator = "FairScan ${BuildConfig.VERSION_NAME}"
|
||||
doc.use { document ->
|
||||
for (jpegBytes in jpegs) {
|
||||
val image = JPEGFactory.createFromByteArray(document, jpegBytes)
|
||||
val image = JPEGFactory.createFromByteArray(document, jpegBytes.get())
|
||||
val page = PDPage(PDRectangle(image.width.toFloat(), image.height.toFloat()))
|
||||
document.addPage(page)
|
||||
val contentStream = PDPageContentStream(document, page, AppendMode.OVERWRITE, false)
|
||||
|
||||
@@ -174,9 +174,9 @@ class ExportViewModel(container: AppContainer, val imageRepository: ImageReposit
|
||||
val jpegs = jpegsForExport(imageRepository, exportQuality)
|
||||
val timestamp = System.currentTimeMillis()
|
||||
preparationDir.mkdirs()
|
||||
val files = jpegs.mapIndexed { index, bytes ->
|
||||
val files = jpegs.mapIndexed { index, jpeg ->
|
||||
val file = File(preparationDir, "$timestamp-${index + 1}.jpg")
|
||||
file.writeBytes(bytes)
|
||||
file.writeBytes(jpeg.get())
|
||||
file
|
||||
}.toList()
|
||||
val sizeInBytes = files.sumOf { it.length() }
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
*/
|
||||
package org.fairscan.app.data
|
||||
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.fairscan.app.domain.JpegProvider
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.io.OutputStream
|
||||
@@ -68,16 +70,16 @@ class FileManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun generatePdf() {
|
||||
fun generatePdf() = runTest {
|
||||
val fakePdfWriter = object : PdfWriter {
|
||||
override fun writePdfFromJpegs(jpegs: Sequence<ByteArray>, outputStream: OutputStream): Int {
|
||||
override suspend fun writePdfFromJpegs(jpegs: List<JpegProvider>, outputStream: OutputStream): Int {
|
||||
val list = jpegs.toList()
|
||||
list.forEach { bytes -> outputStream.write(bytes) }
|
||||
list.forEach { bytes -> outputStream.write(bytes.get()) }
|
||||
return list.size
|
||||
}
|
||||
}
|
||||
val manager = FileManager(pdfDir, externalDir, fakePdfWriter)
|
||||
val jpegs = listOf(byteArrayOf(0x01, 0x02), byteArrayOf(0x11)).asSequence()
|
||||
val jpegs = listOf(byteArrayOf(0x01, 0x02), byteArrayOf(0x11)).map { JpegProvider { it } }
|
||||
val pdf = manager.generatePdf(jpegs)
|
||||
assertThat(pdf.pageCount).isEqualTo(2)
|
||||
assertThat(pdf.sizeInBytes).isEqualTo(3)
|
||||
|
||||
Reference in New Issue
Block a user