PDF renderer library for Android

You can use Nutrient to render a PDF page into a Bitmap(opens in a new tab) object so that you can use it in your custom application views as well. To do this, use Nutrient and PdfDocument class calls.

Initializing Nutrient

Before loading or rendering a document, you have to initialize Nutrient by providing your license. Here’s how to do it.

Loading a document

The PdfDocumentLoader class offers a variety of methods for loading a document. You can load documents from a Uri(opens in a new tab) or a DataProvider. The following example loads a PDF document from the app’s assets. The different available document sources are described in the PdfActivity guide:

val document : PdfDocument
try {
// Use this `Uri` format to access files inside your app's assets.
val documentUri = Uri.parse("file:///android_asset/shopping-center-plan.pdf")
// This synchronously opens the document. To keep your app UI responsive, you should do this call
// on a background thread, or use the asynchronous version of this method instead.
document = PdfDocumentLoader.openDocument(context, documentUri)
} catch (e : IOException) {
handleDocumentLoadingError(e)
}

Now that the PdfDocument has been loaded, you can use it to render PDF pages.

Rendering pages

The rendering of pages can be performed synchronously or asynchronously using the #renderPageToBitmap and #renderPageToBitmapAsync methods of the PdfDocument, respectively.

Synchronous rendering

To synchronously render a page into a Bitmap(opens in a new tab), use any of the available #renderPageToBitmap methods. These methods will block until rendering has been finished, which means you should only use them on a background thread (not from the main UI thread of your app).

💡 Tip: To keep the original page aspect while rendering and prevent stretching of the resulting image, you can access the original PageSize of the rendered page (in PDF points) and calculate your resulting bitmap size:

val pageIndex = 0
// Page size is in PDF points (not pixels).
val pageSize : Size = document.getPageSize(pageIndex)
// We define a target width for the resulting bitmap and use it to calculate the final height.
val width = 2048
val height = (pageSize.height * (width / pageSize.width)).toInt()
// This will render the first page uniformly into a bitmap with a width of 2,048 pixels.
val pageBitmap : Bitmap = document.renderPageToBitmap(context, pageIndex, width, height)

Asynchronous rendering

To render the document asynchronously in a background thread, you can use any of the available #renderPageToBitmapAsync methods. These methods won’t block, but they will instead return an RxJava Single<Bitmap>(opens in a new tab), which will emit the Bitmap(opens in a new tab) once rendering has finished (similar to a callback). The rendering itself is performed on a background thread by default, so you can safely call this method from your app’s main thread:

// Render the page on a background thread and return the resulting bitmap on the main thread.
document.renderPageToBitmapAsync(context, pageIndex, width, height)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { bitmap ->
// Your code can now use the bitmap.
updateUserInterface(bitmap)
}

Providing a render configuration

Both synchronous and asynchronous render methods allow you to specify a PageRenderConfiguration object for defining details about the requested rendering. You can create the configuration using its Builder class. The configuration allows you to specify several options:

  • Enabling or disabling of an in-memory render cache.
  • Rendering of only a specific region on the page (e.g. the upper half).
  • A reuseBitmap object that should be reused for rendering. Use this to optimize memory performance of your app.
  • Background color, grayscale mode, or inversion of all colors.
  • Rendering of renderedDrawables above the page content. This can be used to add watermarks. More information can be found in our drawable API guide.
val renderConfig = PageRenderConfiguration.Builder()
.toGrayscale(true)
.build()
val grayscaleBitmap : Bitmap = document.renderPageToBitmap(context, pageIndex, width, height, renderConfig)

Rendering a specific part of a page

As previously mentioned, by using PageRenderConfiguration, you can render a specific part of a page. See the PageRenderConfiguration.Builder#region() method for more information.

Here’s an example of rendering the part of the page that is currently zoomed (every step is described by a comment):

// You can get all visible pages by calling `PdfFragment#getVisiblePages()`.
val somePageIndex = ...
// In order to render just a part of the page, you first need the visible rect.
val currentVisibleRect = RectF()
// This saves the visible rect in PDF coordinates to `currentVisibleRect`.
pdfFragment.getVisiblePdfRect(currentVisibleRect, somePageIndex)
// Define a desired width for the bitmap in pixels.
val bitmapWidth = 2000
// Now calculate the height, keeping the aspect ratio of the bitmap as the visible area.
val ratio = bitmapWidth / currentVisibleRect.width
// Invert the height so it's not negative (in PDF, the rect top has a higher value than the bottom).
val bitmapHeight = (-currentVisibleRect.height * ratio) as Int
// Calculate the size of the full page for the desired region bitmap size.
val pageSizeInPdfPoints = document.getPageSize(somePageIndex)
val fullPageWidth = (pageSizeInPdfPoints.width * ratio) as Int
val fullPageHeight = (pageSizeInPdfPoints.height * ratio) as Int
// Now we need to get region coordinates in relation to the full page.
val x = (currentVisibleRect.left * ratio) as Int
val y = ((pageSizeInPdfPoints.height - currentVisibleRect.top) * ratio) as Int
// Offsets define the movement of the full page, so we move the page region in the top/left direction
// by x and y and then capture it.
val renderConfiguration = PageRenderConfiguration.Builder()
.region(-x, -y, fullPageWidth, fullPageHeight)
.build()
// Captures the region and renders it to the bitmap.
val regionBitmap = document.renderPageToBitmap(getContext(), 0, bitmapWidth, bitmapHeight, renderConfiguration)