Update dependencies

This commit is contained in:
Luke Hubmayer-Werner 2024-08-14 18:54:28 +09:30
parent 86e12449f7
commit 9a2b5ce297
3 changed files with 152 additions and 8 deletions

View File

@ -3,10 +3,10 @@ Minimum Viable Goals:
- [x] Continuous Horizontal Scroll - unreasonably rare feature
- [x] Presentation View - i.e. show on an external monitor - haven't seen another app do this yet!
- [x] Immersive View - no system status bar etc.
- [x] Autocrop margins
- [ ] Open files the Android way - currently just a bundled test file
- [ ] Delete+Reorder Pages - doesn't have to save the source file, but does need to save a change journal
- [ ] Text Annotations - as above
- [ ] Autocrop margins
Stretch Goals:
- [ ] Networked View:

View File

@ -0,0 +1,144 @@
package com.lhw.pdf
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.pdf.PdfRenderer
import android.os.ParcelFileDescriptor
import androidx.core.graphics.times
import java.io.File
import kotlin.math.max
import kotlin.math.min
class PdfDocument(private val fileCached: File, private val autoCrop: Boolean = false) {
private val pfd = ParcelFileDescriptor.open(fileCached, ParcelFileDescriptor.MODE_READ_ONLY)
private val renderer = PdfRenderer(pfd)
val bitmapThumbnails = mutableMapOf<Int, Bitmap>()
val bitmapPagesMain = mutableMapOf<Int, Bitmap>()
val bitmapPagesPresentation = mutableMapOf<Int, Bitmap>()
private val pagePtWidths = mutableListOf<Int>()
private val pagePtHeights = mutableListOf<Int>()
private val pageAutoCropRects = mutableListOf<RectF>()
// Due to the way thumbnails render, the bottom right is more likely to be cut off
private val autoCropSafetyMarginUpper = 2
private val autoCropSafetyMarginLower = 16
init {
val numPages = renderer.pageCount
for (i in 0..<numPages) {
val page = renderer.openPage(i)
pagePtWidths.add(page.width)
pagePtHeights.add(page.height)
page.close()
}
}
private val rectFIdentity = RectF(0F, 0F, 1F, 1F)
private fun renderPage(index: Int, maxWidth: Int, maxHeight: Int, crop: Boolean = false): Bitmap {
val cropRect = if (!crop || pageAutoCropRects.size <= index) rectFIdentity else pageAutoCropRects[index]
val ptWidth = pagePtWidths[index] * cropRect.width()
val ptHeight = pagePtHeights[index] * cropRect.height()
val widthFromMaxHeight = (ptWidth * maxHeight/ptHeight).toInt()
val heightFromMaxWidth = (ptHeight * maxWidth/ptWidth).toInt()
val (width, height) = if (widthFromMaxHeight > 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()
return bitmap
}
private fun renderPagesToMap(map: MutableMap<Int, Bitmap>, width: Int, height: Int, crop: Boolean = false) {
map.clear()
val numPages = renderer.pageCount
for (i in 0..<numPages) {
map[i] = renderPage(i, width, height, crop)
}
}
private fun isColorContent(c: Int): Boolean {
val color = Color.valueOf(c)
return (color.alpha() > 0) && (color.luminance() < 0.9F)
}
fun renderThumbnails(width: Int, height: Int) {
renderPagesToMap(bitmapThumbnails, width, height)
println("Thumbnails rendered")
if (autoCrop) {
println("Auto cropping from thumbnails")
val numPages = renderer.pageCount
pageAutoCropRects.clear()
for (i in 0..<numPages) {
println("Auto cropping page $i from thumbnail")
val bitmap = bitmapThumbnails[i]!!
val w = bitmap.width
val h = bitmap.height
val rect = Rect()
val rectF = RectF(rectFIdentity)
// Find first row
top@ for (y in 0..<h) {
for (x in 0..<w) {
val c = bitmap.getPixel(x, y)
// println("Auto crop checking pixel $x $y == $c")
if (isColorContent(c)) {
rect.top = max(y-autoCropSafetyMarginUpper, 0)
rectF.top = rect.top/h.toFloat()
break@top
}
}
}
// Find last row
bottom@ for (y in (h-1) downTo 0) {
for (x in 0..<w) {
val c = bitmap.getPixel(x, y)
if (isColorContent(c)) {
rect.bottom = min(y+autoCropSafetyMarginLower, h-1)
rectF.bottom = (rect.bottom+1)/h.toFloat()
break@bottom
}
}
}
// Find first column
left@ for (x in 0..<w) {
for (y in rect.top..rect.bottom) {
val c = bitmap.getPixel(x, y)
if (isColorContent(c)) {
rect.left = max(x-autoCropSafetyMarginUpper, 0)
rectF.left = rect.left/w.toFloat()
break@left
}
}
}
// Find last column
right@ for (x in (w-1) downTo 0) {
for (y in rect.top..rect.bottom) {
val c = bitmap.getPixel(x, y)
if (isColorContent(c)) {
rect.right = min(x+autoCropSafetyMarginLower, w-1)
rectF.right = (rect.right+1)/w.toFloat()
break@right
}
}
}
println("Auto crop found margins of page $i at $rect, scales to $rectF")
pageAutoCropRects.add(rectF)
}
}
}
fun renderPagesMain(width: Int, height: Int) {
renderPagesToMap(bitmapPagesMain, width, height, true)
}
fun renderPagesPresentation(width: Int, height: Int) {
renderPagesToMap(bitmapPagesPresentation, width, height, true)
}
}

View File

@ -3,15 +3,15 @@ agp = "8.5.2"
kotlin = "1.9.0"
coreKtx = "1.13.1"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
appcompat = "1.7.0"
material = "1.10.0"
material = "1.12.0"
constraintlayout = "2.1.4"
lifecycleLivedataKtx = "2.6.1"
lifecycleViewmodelKtx = "2.6.1"
navigationFragmentKtx = "2.6.0"
navigationUiKtx = "2.6.0"
lifecycleLivedataKtx = "2.8.4"
lifecycleViewmodelKtx = "2.8.4"
navigationFragmentKtx = "2.7.7"
navigationUiKtx = "2.7.7"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }