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