Home | History | Annotate | Download | only in pdfrendererbasic
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.pdfrendererbasic
     18 
     19 import android.content.Context
     20 import android.graphics.Bitmap
     21 import android.graphics.Bitmap.createBitmap
     22 import android.graphics.pdf.PdfRenderer
     23 import android.os.Bundle
     24 import android.os.ParcelFileDescriptor
     25 import android.support.v4.app.Fragment
     26 import android.util.Log
     27 import android.view.LayoutInflater
     28 import android.view.View
     29 import android.view.ViewGroup
     30 import android.widget.Button
     31 import android.widget.ImageView
     32 import java.io.File
     33 import java.io.FileOutputStream
     34 import java.io.IOException
     35 
     36 /**
     37  * This fragment has a big [ImageView] that shows PDF pages, and 2 [Button]s to move between pages.
     38  * We use a [PdfRenderer] to render PDF pages as [Bitmap]s.
     39  */
     40 class PdfRendererBasicFragment : Fragment(), View.OnClickListener {
     41 
     42     /**
     43      * The filename of the PDF.
     44      */
     45     private val FILENAME = "sample.pdf"
     46 
     47     /**
     48      * Key string for saving the state of current page index.
     49      */
     50     private val STATE_CURRENT_PAGE_INDEX = "current_page_index"
     51 
     52     /**
     53      * String for logging.
     54      */
     55     private val TAG = "PdfRendererBasicFragment"
     56 
     57     /**
     58      * The initial page index of the PDF.
     59      */
     60     private val INITIAL_PAGE_INDEX = 0
     61 
     62     /**
     63      * File descriptor of the PDF.
     64      */
     65     private lateinit var fileDescriptor: ParcelFileDescriptor
     66 
     67     /**
     68      * [PdfRenderer] to render the PDF.
     69      */
     70     private lateinit var pdfRenderer: PdfRenderer
     71 
     72     /**
     73      * Page that is currently shown on the screen.
     74      */
     75     private lateinit var currentPage: PdfRenderer.Page
     76 
     77     /**
     78      * [ImageView] that shows a PDF page as a [Bitmap].
     79      */
     80     private lateinit var imageView: ImageView
     81 
     82     /**
     83      * [Button] to move to the previous page.
     84      */
     85     private lateinit var btnPrevious: Button
     86 
     87     /**
     88      * [Button] to move to the next page.
     89      */
     90     private lateinit var btnNext: Button
     91 
     92     /**
     93      * PDF page index.
     94      */
     95     private var pageIndex: Int = INITIAL_PAGE_INDEX
     96 
     97     override fun onCreateView(
     98             inflater: LayoutInflater,
     99             container: ViewGroup?,
    100             savedInstanceState: Bundle?
    101     ): View? {
    102         return inflater.inflate(R.layout.fragment_pdf_renderer_basic, container, false)
    103     }
    104 
    105     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    106         super.onViewCreated(view, savedInstanceState)
    107         imageView = view.findViewById(R.id.image)
    108         btnPrevious = view.findViewById<Button>(R.id.previous).also { it.setOnClickListener(this) }
    109         btnNext = view.findViewById<Button>(R.id.next).also { it.setOnClickListener(this)}
    110 
    111         // If there is a savedInstanceState (screen orientations, etc.), we restore the page index.
    112         if (savedInstanceState != null) {
    113             pageIndex = savedInstanceState.getInt(STATE_CURRENT_PAGE_INDEX, INITIAL_PAGE_INDEX)
    114         } else {
    115             pageIndex = INITIAL_PAGE_INDEX
    116         }
    117     }
    118 
    119     override fun onStart() {
    120         super.onStart()
    121         try {
    122             openRenderer(activity)
    123             showPage(pageIndex)
    124         } catch (e: IOException) {
    125             Log.d(TAG, e.toString())
    126         }
    127     }
    128 
    129     override fun onStop() {
    130         try {
    131             closeRenderer()
    132         } catch (e: IOException) {
    133             Log.d(TAG, e.toString())
    134         }
    135         super.onStop()
    136     }
    137 
    138     override fun onSaveInstanceState(outState: Bundle) {
    139         outState.putInt(STATE_CURRENT_PAGE_INDEX, currentPage.index)
    140         super.onSaveInstanceState(outState)
    141     }
    142 
    143     /**
    144      * Sets up a [PdfRenderer] and related resources.
    145      */
    146     @Throws(IOException::class)
    147     private fun openRenderer(context: Context?) {
    148         if (context == null) return
    149 
    150         // In this sample, we read a PDF from the assets directory.
    151         val file = File(context.cacheDir, FILENAME)
    152         if (!file.exists()) {
    153             // Since PdfRenderer cannot handle the compressed asset file directly, we copy it into
    154             // the cache directory.
    155             val asset = context.assets.open(FILENAME)
    156             val output = FileOutputStream(file)
    157             val buffer = ByteArray(1024)
    158             var size = asset.read(buffer)
    159             while (size != -1) {
    160                 output.write(buffer, 0, size)
    161                 size = asset.read(buffer)
    162             }
    163             asset.close()
    164             output.close()
    165         }
    166         fileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
    167         // This is the PdfRenderer we use to render the PDF.
    168         pdfRenderer = PdfRenderer(fileDescriptor)
    169         currentPage = pdfRenderer.openPage(pageIndex)
    170     }
    171 
    172     /**
    173      * Closes the [PdfRenderer] and related resources.
    174      *
    175      * @throws IOException When the PDF file cannot be closed.
    176      */
    177     @Throws(IOException::class)
    178     private fun closeRenderer() {
    179         currentPage.close()
    180         pdfRenderer.close()
    181         fileDescriptor.close()
    182     }
    183 
    184     /**
    185      * Shows the specified page of PDF to the screen.
    186      *
    187      * @param index The page index.
    188      */
    189     private fun showPage(index: Int) {
    190         if (pdfRenderer.pageCount <= index) return
    191 
    192         // Make sure to close the current page before opening another one.
    193         currentPage.close()
    194         // Use `openPage` to open a specific page in PDF.
    195         currentPage = pdfRenderer.openPage(index)
    196         // Important: the destination bitmap must be ARGB (not RGB).
    197         val bitmap = createBitmap(currentPage.width, currentPage.height, Bitmap.Config.ARGB_8888)
    198         // Here, we render the page onto the Bitmap.
    199         // To render a portion of the page, use the second and third parameter. Pass nulls to get
    200         // the default result.
    201         // Pass either RENDER_MODE_FOR_DISPLAY or RENDER_MODE_FOR_PRINT for the last parameter.
    202         currentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
    203         // We are ready to show the Bitmap to user.
    204         imageView.setImageBitmap(bitmap)
    205         updateUi()
    206     }
    207 
    208     /**
    209      * Updates the state of 2 control buttons in response to the current page index.
    210      */
    211     private fun updateUi() {
    212         val index = currentPage.index
    213         val pageCount = pdfRenderer.pageCount
    214         btnPrevious.isEnabled = (0 != index)
    215         btnNext.isEnabled = (index + 1 < pageCount)
    216         activity?.title = getString(R.string.app_name_with_index, index + 1, pageCount)
    217     }
    218 
    219     /**
    220      * Returns the page count of of the PDF.
    221      */
    222     fun getPageCount() = pdfRenderer.pageCount
    223 
    224     override fun onClick(view: View) {
    225         when (view.id) {
    226             R.id.previous -> {
    227                 // Move to the previous page/
    228                 showPage(currentPage.index - 1)
    229             }
    230             R.id.next -> {
    231                 // Move to the next page.
    232                 showPage(currentPage.index + 1)
    233             }
    234         }
    235     }
    236 
    237 }
    238