From 8b3551e141d0dcc052ea19122bbbc03a0ded17ee Mon Sep 17 00:00:00 2001 From: arcanegolem Date: Mon, 6 May 2024 04:03:26 +0300 Subject: [PATCH] Added zoom controls to PdfViewer, as well as parameters for customizing them + bitmap scale for documents with tiny details or text --- .../ru/spektrit/composepdf/MainActivity.kt | 6 +- .../lycoris/viewdocument/PdfViewer.kt | 32 ++++++- .../lycoris/viewdocument/PdfViewerDisplay.kt | 83 ++++++++++++++----- 3 files changed, 96 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/ru/spektrit/composepdf/MainActivity.kt b/app/src/main/java/ru/spektrit/composepdf/MainActivity.kt index 178f49a..d6451f6 100644 --- a/app/src/main/java/ru/spektrit/composepdf/MainActivity.kt +++ b/app/src/main/java/ru/spektrit/composepdf/MainActivity.kt @@ -5,6 +5,7 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import ru.spektrit.composepdf.ui.theme.ComposePDFTheme import ru.spektrit.lycoris.viewdocument.PdfViewer @@ -17,7 +18,10 @@ class MainActivity : ComponentActivity() { Column( modifier = Modifier.fillMaxSize(), ) { - PdfViewer(pdfResId = R.raw.sample_multipage) + PdfViewer( + pdfResId = R.raw.horizontal, + controlsAlignment = Alignment.CenterEnd, + ) } } } diff --git a/lycoris/src/main/java/ru/spektrit/lycoris/viewdocument/PdfViewer.kt b/lycoris/src/main/java/ru/spektrit/lycoris/viewdocument/PdfViewer.kt index 04451b3..eec2572 100644 --- a/lycoris/src/main/java/ru/spektrit/lycoris/viewdocument/PdfViewer.kt +++ b/lycoris/src/main/java/ru/spektrit/lycoris/viewdocument/PdfViewer.kt @@ -10,7 +10,9 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import kotlinx.coroutines.Dispatchers @@ -34,6 +36,10 @@ fun PdfViewer( modifier: Modifier = Modifier, @RawRes pdfResId: Int, verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(8.dp), + iconTint : Color = Color.Black, + accentColor : Color = Color.DarkGray, + controlsAlignment: Alignment = Alignment.BottomEnd, + bitmapScale : Int = 1 ) { val documentIdentifier = pdfResId.toString() @@ -58,12 +64,16 @@ fun PdfViewer( } PdfViewerDisplay( + accentColor = accentColor, modifier = modifier, context = context, renderer = renderer, documentIdentifier = documentIdentifier, verticalArrangement = verticalArrangement, - mutex = mutex + mutex = mutex, + controlsAlignment = controlsAlignment, + iconTint = iconTint, + bitmapScale = bitmapScale ) } @@ -80,6 +90,10 @@ fun PdfViewer( modifier: Modifier = Modifier, uri: Uri, verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(8.dp), + accentColor: Color = Color.DarkGray, + iconTint: Color = Color.Black, + controlsAlignment: Alignment = Alignment.BottomEnd, + bitmapScale : Int = 1 ) { val rendererScope = rememberCoroutineScope() val mutex = remember { Mutex() } @@ -100,12 +114,16 @@ fun PdfViewer( } PdfViewerDisplay( + accentColor = accentColor, modifier = modifier, context = context, renderer = renderer, documentIdentifier = uri.toString(), verticalArrangement = verticalArrangement, - mutex = mutex + mutex = mutex, + controlsAlignment = controlsAlignment, + iconTint = iconTint, + bitmapScale = bitmapScale ) } @@ -124,6 +142,10 @@ fun PdfViewer( @Url url: String, headers: HashMap? = null, verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(8.dp), + accentColor: Color = Color.DarkGray, + iconTint: Color = Color.Black, + controlsAlignment: Alignment = Alignment.BottomEnd, + bitmapScale : Int = 1 ) { val rendererScope = rememberCoroutineScope() @@ -144,12 +166,16 @@ fun PdfViewer( } PdfViewerDisplay( + accentColor = accentColor, modifier = modifier, context = context, renderer = renderer, documentIdentifier = url, verticalArrangement = verticalArrangement, - mutex = mutex + mutex = mutex, + controlsAlignment = controlsAlignment, + iconTint = iconTint, + bitmapScale = bitmapScale ) } diff --git a/lycoris/src/main/java/ru/spektrit/lycoris/viewdocument/PdfViewerDisplay.kt b/lycoris/src/main/java/ru/spektrit/lycoris/viewdocument/PdfViewerDisplay.kt index d79df37..d501973 100644 --- a/lycoris/src/main/java/ru/spektrit/lycoris/viewdocument/PdfViewerDisplay.kt +++ b/lycoris/src/main/java/ru/spektrit/lycoris/viewdocument/PdfViewerDisplay.kt @@ -7,10 +7,12 @@ import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.gestures.rememberTransformableState +import androidx.compose.foundation.gestures.scrollBy import androidx.compose.foundation.gestures.transformable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxWidth @@ -19,8 +21,11 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Search +import androidx.compose.material.icons.rounded.ZoomIn +import androidx.compose.material.icons.rounded.ZoomOut import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -59,7 +64,13 @@ internal fun PdfViewerDisplay( documentIdentifier : String, verticalArrangement: Arrangement.Vertical, mutex : Mutex, + accentColor : Color, + iconTint : Color, + controlsAlignment: Alignment, + bitmapScale : Int ) { + val pdfViewerDisplayScope = rememberCoroutineScope() + val imageLoader = context.imageLoader val imageLoadingScope = rememberCoroutineScope() @@ -95,9 +106,45 @@ internal fun PdfViewerDisplay( } - val transformableState = rememberTransformableState { zoomChange, panChange, _ -> - viewerScale = (viewerScale * zoomChange).coerceIn(1f, 10f) + if (pageCount > 2) { + Column( + modifier = Modifier + .align(controlsAlignment) + .padding(8.dp) + .zIndex(2f) + ) { + IconButton( + onClick = { viewerScale = (viewerScale + 0.5f).coerceIn(1f, 10f) }, + colors = IconButtonDefaults.iconButtonColors( + containerColor = accentColor.copy(alpha = 0.4f) + ) + ) { + Icon( + imageVector = Icons.Rounded.ZoomIn, + contentDescription = "Zoom in", + tint = iconTint + ) + } + IconButton( + onClick = { + viewerScale = 1f + viewerOffset = Offset(0f, 0f) + }, + colors = IconButtonDefaults.iconButtonColors( + containerColor = accentColor.copy(alpha = 0.4f) + ) + ) { + Icon( + imageVector = Icons.Rounded.ZoomOut, + contentDescription = "Zoom out", + tint = iconTint + ) + } + } + } + + val transformableState = rememberTransformableState { _, panChange, _ -> val extraWidth = (viewerScale - 1) * constraints.maxWidth val extraHeight = (viewerScale - 1) * constraints.maxHeight @@ -106,8 +153,9 @@ internal fun PdfViewerDisplay( viewerOffset = Offset( x = (viewerOffset.x + panChange.x * viewerScale).coerceIn(-maxX, maxX), - y = (viewerOffset.y + panChange.y * viewerScale).coerceIn(-maxY, maxY), + y = (viewerOffset.y + (panChange.y / 2) * viewerScale).coerceIn(-maxY, maxY), ) + pdfViewerDisplayScope.launch{ lazyListState.scrollBy(-(panChange.y / 2)) } } LazyColumn( userScrollEnabled = viewerScale == 1f, @@ -146,7 +194,7 @@ internal fun PdfViewerDisplay( try { renderer?.let { it.openPage(index).use { page -> - destinationBitmap = Bitmap.createBitmap(page.width, page.height, Bitmap.Config.ARGB_8888) + destinationBitmap = Bitmap.createBitmap(page.width * bitmapScale, page.height * bitmapScale, Bitmap.Config.ARGB_8888) page.render(destinationBitmap!!, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY) } } @@ -171,15 +219,6 @@ internal fun PdfViewerDisplay( .build() Box { - Icon( - modifier = Modifier - .align(Alignment.BottomEnd) - .padding(15.dp) - .zIndex(1f), - imageVector = Icons.Rounded.Search, - contentDescription = "Magnify Indicator" - ) - Image( modifier = Modifier .background(Color.White) @@ -198,12 +237,14 @@ internal fun PdfViewerDisplay( } } - Text( - modifier = Modifier - .align(Alignment.TopEnd) - .padding(10.dp) - .zIndex(1f), - text = "${currentPage + 1}/$pageCount" - ) + if (pageCount > 2) { + Text( + modifier = Modifier + .align(Alignment.TopEnd) + .padding(10.dp) + .zIndex(1f), + text = "${currentPage + 1}/$pageCount" + ) + } } } \ No newline at end of file