From 04a64bd6215f58a982e87db46155e3aa6161ff1a Mon Sep 17 00:00:00 2001 From: Luke Hubmayer-Werner Date: Sat, 24 Aug 2024 18:10:26 +0930 Subject: [PATCH] Add PdfBox library for future parsing Change buttons to chips --- app/build.gradle.kts | 2 + app/src/main/java/com/lhw/pdf/MainActivity.kt | 47 ++-- app/src/main/java/com/lhw/pdf/PdfDocument.kt | 52 +++-- app/src/main/res/layout/app_bar_main.xml | 35 --- app/src/main/res/layout/content_main.xml | 201 +++++++++++++----- gradle/libs.versions.toml | 2 + 6 files changed, 220 insertions(+), 119 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 66aaaaa..cb90666 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -51,4 +51,6 @@ dependencies { testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) + + implementation(libs.pdfbox.android) } \ No newline at end of file diff --git a/app/src/main/java/com/lhw/pdf/MainActivity.kt b/app/src/main/java/com/lhw/pdf/MainActivity.kt index 9ee902a..1b37877 100644 --- a/app/src/main/java/com/lhw/pdf/MainActivity.kt +++ b/app/src/main/java/com/lhw/pdf/MainActivity.kt @@ -11,6 +11,8 @@ import android.view.Display import android.view.Menu import android.view.WindowManager import android.widget.ImageView +import androidx.activity.SystemBarStyle +import androidx.activity.enableEdgeToEdge import com.google.android.material.snackbar.Snackbar import com.google.android.material.navigation.NavigationView import androidx.navigation.findNavController @@ -23,6 +25,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import com.lhw.pdf.databinding.ActivityMainBinding +import com.tom_roush.pdfbox.android.PDFBoxResourceLoader import java.io.File import java.io.FileOutputStream import java.io.InputStream @@ -37,6 +40,11 @@ class MainActivity : AppCompatActivity() { private val thumbnailHeight = 700 private var renderAutoCrop = true private var pagesPerLandscape = 3F + private set(pages) { + field = pages + updatePresentations() + } + private var usePdfBox = false private val presentations = mutableMapOf() private lateinit var pdfDocument: PdfDocument private val showPages = mutableListOf() @@ -151,12 +159,15 @@ class MainActivity : AppCompatActivity() { } override fun onCreate(savedInstanceState: Bundle?) { + //enableEdgeToEdge() super.onCreate(savedInstanceState) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager //val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView) //windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()) //Doesn't seem to actually fill the vacant space by default + PDFBoxResourceLoader.init(this) + binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) @@ -169,7 +180,7 @@ class MainActivity : AppCompatActivity() { if (!file.exists()) { file = inputStreamToCache(defaultCachedFileName, resources.openRawResource(R.raw.testpdf)) } - pdfDocument = PdfDocument(file, true) + pdfDocument = PdfDocument(file, usePdfBox = false, autoCrop = true) // Then overwrite it with any pdf sent via intent handleIntent(intent) @@ -184,26 +195,24 @@ class MainActivity : AppCompatActivity() { presentations.values.forEach {it.setScrollProgress(progress)} } - binding.appBarMain.crop.setOnClickListener { view -> - renderAutoCrop = !renderAutoCrop - val s = if (renderAutoCrop) "On" else "Off" - Snackbar.make(view, "Toggling Auto Crop $s", Snackbar.LENGTH_LONG) - .setAction("Action", null) - .setAnchorView(R.id.crop).show() + binding.appBarMain.contentMain.chipPages1.setOnCheckedChangeListener { _, checked -> if (checked) pagesPerLandscape = 1F } + binding.appBarMain.contentMain.chipPages15.setOnCheckedChangeListener { _, checked -> if (checked) pagesPerLandscape = 1.5F } + binding.appBarMain.contentMain.chipPages2.setOnCheckedChangeListener { _, checked -> if (checked) pagesPerLandscape = 2F } + binding.appBarMain.contentMain.chipPages25.setOnCheckedChangeListener { _, checked -> if (checked) pagesPerLandscape = 2.5F } + binding.appBarMain.contentMain.chipPages3.setOnCheckedChangeListener { _, checked -> if (checked) pagesPerLandscape = 3F } + binding.appBarMain.contentMain.chipPages35.setOnCheckedChangeListener { _, checked -> if (checked) pagesPerLandscape = 3.5F } + binding.appBarMain.contentMain.chipPages4.setOnCheckedChangeListener { _, checked -> if (checked) pagesPerLandscape = 4F } + binding.appBarMain.contentMain.chipPages5.setOnCheckedChangeListener { _, checked -> if (checked) pagesPerLandscape = 5F } + binding.appBarMain.contentMain.chipPages6.setOnCheckedChangeListener { _, checked -> if (checked) pagesPerLandscape = 6F } + binding.appBarMain.contentMain.chipPages8.setOnCheckedChangeListener { _, checked -> if (checked) pagesPerLandscape = 8F } + binding.appBarMain.contentMain.chipPages10.setOnCheckedChangeListener { _, checked -> if (checked) pagesPerLandscape = 10F } + binding.appBarMain.contentMain.chipAutoCrop.setOnCheckedChangeListener { _, checked -> + renderAutoCrop = checked updatePresentations() } - binding.appBarMain.zoomIn.setOnClickListener { view -> - pagesPerLandscape = max(pagesPerLandscape - 0.5F, 1F) - Snackbar.make(view, "Aiming for $pagesPerLandscape pages at a time", Snackbar.LENGTH_LONG) - .setAction("Action", null) - .setAnchorView(R.id.zoom_in).show() - updatePresentations() - } - binding.appBarMain.zoomOut.setOnClickListener { view -> - pagesPerLandscape = min(pagesPerLandscape + 0.5F, 10F) - Snackbar.make(view, "Aiming for $pagesPerLandscape pages at a time", Snackbar.LENGTH_LONG) - .setAction("Action", null) - .setAnchorView(R.id.zoom_out).show() + binding.appBarMain.contentMain.chipUsePdfBox.setOnCheckedChangeListener { _, checked -> + usePdfBox = checked + pdfDocument.usePdfBox = usePdfBox updatePresentations() } val drawerLayout: DrawerLayout = binding.drawerLayout diff --git a/app/src/main/java/com/lhw/pdf/PdfDocument.kt b/app/src/main/java/com/lhw/pdf/PdfDocument.kt index 7708e34..4c0342d 100644 --- a/app/src/main/java/com/lhw/pdf/PdfDocument.kt +++ b/app/src/main/java/com/lhw/pdf/PdfDocument.kt @@ -7,25 +7,40 @@ import android.graphics.Rect import android.graphics.RectF import android.graphics.pdf.PdfRenderer import android.os.ParcelFileDescriptor +import com.tom_roush.pdfbox.pdmodel.PDDocument +import com.tom_roush.pdfbox.rendering.PDFRenderer import java.io.File import kotlin.math.max import kotlin.math.min -class PdfDocument(private val fileCached: File, private val autoCrop: Boolean = false) { +class PdfDocument(private val fileCached: File, var usePdfBox: Boolean = true, private val autoCrop: Boolean = false) { + // Stock Android PdfRenderer - fast but limited private val pfd = ParcelFileDescriptor.open(fileCached, ParcelFileDescriptor.MODE_READ_ONLY) private val renderer = PdfRenderer(pfd) - val bitmapThumbnails = mutableMapOf() - val bitmapPages = mutableMapOf, MutableMap>() // bitmapPages[][pageId] private val pagePtWidths = mutableListOf() private val pagePtHeights = mutableListOf() + // PdfBox parser/renderer - powerful but slow + private val pdfDocument = PDDocument.load(fileCached) + private val boxRenderer = PDFRenderer(pdfDocument) + private val boxPagePtWidths = mutableListOf() + private val boxPagePtHeights = mutableListOf() + + val bitmapThumbnails = mutableMapOf() + val bitmapPages = mutableMapOf, MutableMap>() // bitmapPages[][pageId] private val pageAutoCropRects = mutableListOf() // Due to the way thumbnails render, the bottom right is more likely to be cut off private val autoCropSafetyMarginUpper = 2 private val autoCropSafetyMarginLower = 16 + private val numPages get() = if (usePdfBox) pdfDocument.numberOfPages else renderer.pageCount + init { - val numPages = renderer.pageCount for (i in 0.. maxWidth) Pair(maxWidth, heightFromMaxWidth) else Pair(widthFromMaxHeight, maxHeight) - val page = renderer.openPage(index) - println("Creating page $index bitmap with width $width and height $height from cropRect $cropRect") - val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) - val ptToPixel = height/ptHeight - val transform = Matrix() - val transposePtX = -cropRect.left * ptWidth - val transposePtY = -cropRect.top * ptHeight - transform.setValues(floatArrayOf(ptToPixel, 0F, transposePtX*ptToPixel, 0F, ptToPixel, transposePtY*ptToPixel, 0F, 0F, 1F)) - page.render(bitmap, null, transform, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY) - page.close() + val bitmap: Bitmap + if (usePdfBox) { + val scale = width / ptWidth + bitmap = boxRenderer.renderImage(index, scale) + } else { + val page = renderer.openPage(index) + println("Creating page $index bitmap with width $width and height $height from cropRect $cropRect") + bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + val ptToPixel = height / ptHeight + val transform = Matrix() + val transposePtX = -cropRect.left * ptWidth + val transposePtY = -cropRect.top * ptHeight + transform.setValues(floatArrayOf(ptToPixel, 0F, transposePtX * ptToPixel, 0F, ptToPixel, transposePtY * ptToPixel, 0F, 0F, 1F)) + page.render(bitmap, null, transform, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY) + page.close() + } return bitmap } private fun renderPagesToMap(map: MutableMap, width: Int, height: Int, crop: Boolean = false, overwrite: Boolean = true) { if (overwrite) map.clear() - val numPages = renderer.pageCount for (i in 0.. - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml index 8b41bc3..4c58502 100644 --- a/app/src/main/res/layout/content_main.xml +++ b/app/src/main/res/layout/content_main.xml @@ -18,54 +18,157 @@ app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/mobile_navigation" /> - - - - - - - - - - - + android:layout_height="match_parent" + android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 34b4f89..08538c9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,6 +12,7 @@ lifecycleLivedataKtx = "2.8.4" lifecycleViewmodelKtx = "2.8.4" navigationFragmentKtx = "2.7.7" navigationUiKtx = "2.7.7" +pdfboxAndroid = "2.0.27.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -25,6 +26,7 @@ androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecy androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" } androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigationFragmentKtx" } androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" } +pdfbox-android = { module = "com.tom-roush:pdfbox-android", version.ref = "pdfboxAndroid" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }