what is the best way to display a pdf with jetpack...
# compose
y
what is the best way to display a pdf with jetpack compose ?
g
It renders pages into Bitmap, so you can show this bitmap using bitmap.asImageBitmap()
b
Here's my composable for a PDF page, feel free to copy, you just need to provide the renderer and a Mutex (since PdfRenderer can only render on-page-at a time):
Copy code
@Composable
private fun PDFPage(
    renderer: PdfRenderer,
    rendererMutex: Mutex,
    index: Int,
    pageFit: PageFit
) {
    BoxWithConstraints(Modifier.padding(HALF_PAGE_PADDING)) {
        val bitmap by produceState<Bitmap?>(null) {
            withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
                rendererMutex.withLock {
                    renderer.openPage(index).use { page ->
                        val size = pageFit.size(constraints, page.width, page.height)
                        val bitmap = Bitmap.createBitmap(
                            size.width, size.height, Bitmap.Config.ARGB_8888
                        )
                        page.render(
                            bitmap,
                            null,
                            null,
                            PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY
                        )
                        value = bitmap
                    }
                }
            }
        }
        val imageBitmap = bitmap?.asImageBitmap()
        if (imageBitmap == null) Box(
            when (pageFit) {
                PageFit.FILL_HORIZONTAL -> Modifier.fillMaxWidth()
                PageFit.FILL_VERTICAL -> Modifier.fillMaxHeight()
                else -> Modifier
            }.aspectRatio(1f)
        )
        else Image(
            imageBitmap,
            "Page ${index + 1}",
            modifier = Modifier
                .thenIf(pageFit == PageFit.CONTAIN) { align(Alignment.Center) }
                .shadow(4.dp)
                .background(Color.White)
        )
    }
}


private enum class PageFit(val size: (constraints: Constraints, pageWidth: Int, pageHeight: Int) -> IntSize) {
    FILL_HORIZONTAL({ constraints, pageWidth, pageHeight ->
        IntSize(
            constraints.maxWidth,
            (constraints.maxWidth.toFloat() / pageWidth * pageHeight).roundToInt()
        )
    }),
    FILL_VERTICAL({ constraints, pageWidth, pageHeight ->
        IntSize(
            (constraints.maxHeight.toFloat() / pageHeight * pageWidth).roundToInt(),
            constraints.maxHeight,
        )
    }),
    CONTAIN({ constraints, pageWidth, pageHeight ->
        if (pageWidth.toFloat() / constraints.maxWidth > pageHeight.toFloat() / constraints.maxHeight) FILL_VERTICAL.size(
            constraints,
            pageWidth,
            pageHeight
        )
        else FILL_HORIZONTAL.size(constraints, pageWidth, pageHeight)
    })
}
Works great inside a LazyRow/Column or Horizontal/VerticalPager
a
Note though above will allow you to display simply but with super basic rendering of pdf. The system API is capable of more sophisticated tile based interactive rendering, but it's a lot more complex to implement by following the PDF official specs.
For Android your best bet with in-app rendering is the open source and paid libraries, if your app is not a pdf reader app. Or show externally like opening google drive app, or render it via WebView with a special google drive link
b
My code will work great for a Google Drive like PDF experience. You're right that if you zoom in it won't be as detailed since it won't load higher res tiles. I didn't need that for my use case but you can probably add it using https://github.com/davemorrissey/subsampling-scale-image-view
a
Yep. And PDF can be interactive too like having hyperlinks. So it really depends on the use case. iOS has a much better simple way to render PDFs. And anything more advanced would need a paid solution.
💯 1
2085 Views