1 /* 2 * Copyright (C) 2014 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.pdf.PdfRenderer; 22 import android.os.Bundle; 23 import android.os.ParcelFileDescriptor; 24 import android.support.v4.app.Fragment; 25 import android.view.LayoutInflater; 26 import android.view.View; 27 import android.view.ViewGroup; 28 import android.widget.Button; 29 import android.widget.ImageView; 30 import android.widget.Toast; 31 32 import java.io.File; 33 import java.io.FileOutputStream; 34 import java.io.IOException; 35 import java.io.InputStream; 36 37 /** 38 * This fragment has a big {@ImageView} that shows PDF pages, and 2 39 * {@link android.widget.Button}s to move between pages. We use a 40 * {@link android.graphics.pdf.PdfRenderer} to render PDF pages as 41 * {@link android.graphics.Bitmap}s. 42 */ 43 public class PdfRendererBasicFragment extends Fragment implements View.OnClickListener { 44 45 /** 46 * Key string for saving the state of current page index. 47 */ 48 private static final String STATE_CURRENT_PAGE_INDEX = "current_page_index"; 49 50 /** 51 * The filename of the PDF. 52 */ 53 private static final String FILENAME = "sample.pdf"; 54 55 /** 56 * File descriptor of the PDF. 57 */ 58 private ParcelFileDescriptor mFileDescriptor; 59 60 /** 61 * {@link android.graphics.pdf.PdfRenderer} to render the PDF. 62 */ 63 private PdfRenderer mPdfRenderer; 64 65 /** 66 * Page that is currently shown on the screen. 67 */ 68 private PdfRenderer.Page mCurrentPage; 69 70 /** 71 * {@link android.widget.ImageView} that shows a PDF page as a {@link android.graphics.Bitmap} 72 */ 73 private ImageView mImageView; 74 75 /** 76 * {@link android.widget.Button} to move to the previous page. 77 */ 78 private Button mButtonPrevious; 79 80 /** 81 * {@link android.widget.Button} to move to the next page. 82 */ 83 private Button mButtonNext; 84 85 /** 86 * PDF page index 87 */ 88 private int mPageIndex; 89 90 public PdfRendererBasicFragment() { 91 } 92 93 @Override 94 public View onCreateView(LayoutInflater inflater, ViewGroup container, 95 Bundle savedInstanceState) { 96 return inflater.inflate(R.layout.fragment_pdf_renderer_basic, container, false); 97 } 98 99 @Override 100 public void onViewCreated(View view, Bundle savedInstanceState) { 101 super.onViewCreated(view, savedInstanceState); 102 // Retain view references. 103 mImageView = (ImageView) view.findViewById(R.id.image); 104 mButtonPrevious = (Button) view.findViewById(R.id.previous); 105 mButtonNext = (Button) view.findViewById(R.id.next); 106 // Bind events. 107 mButtonPrevious.setOnClickListener(this); 108 mButtonNext.setOnClickListener(this); 109 110 mPageIndex = 0; 111 // If there is a savedInstanceState (screen orientations, etc.), we restore the page index. 112 if (null != savedInstanceState) { 113 mPageIndex = savedInstanceState.getInt(STATE_CURRENT_PAGE_INDEX, 0); 114 } 115 } 116 117 @Override 118 public void onStart() { 119 super.onStart(); 120 try { 121 openRenderer(getActivity()); 122 showPage(mPageIndex); 123 } catch (IOException e) { 124 e.printStackTrace(); 125 Toast.makeText(getActivity(), "Error! " + e.getMessage(), Toast.LENGTH_SHORT).show(); 126 } 127 } 128 129 @Override 130 public void onStop() { 131 try { 132 closeRenderer(); 133 } catch (IOException e) { 134 e.printStackTrace(); 135 } 136 super.onStop(); 137 } 138 139 @Override 140 public void onSaveInstanceState(Bundle outState) { 141 super.onSaveInstanceState(outState); 142 if (null != mCurrentPage) { 143 outState.putInt(STATE_CURRENT_PAGE_INDEX, mCurrentPage.getIndex()); 144 } 145 } 146 147 /** 148 * Sets up a {@link android.graphics.pdf.PdfRenderer} and related resources. 149 */ 150 private void openRenderer(Context context) throws IOException { 151 // In this sample, we read a PDF from the assets directory. 152 File file = new File(context.getCacheDir(), FILENAME); 153 if (!file.exists()) { 154 // Since PdfRenderer cannot handle the compressed asset file directly, we copy it into 155 // the cache directory. 156 InputStream asset = context.getAssets().open(FILENAME); 157 FileOutputStream output = new FileOutputStream(file); 158 final byte[] buffer = new byte[1024]; 159 int size; 160 while ((size = asset.read(buffer)) != -1) { 161 output.write(buffer, 0, size); 162 } 163 asset.close(); 164 output.close(); 165 } 166 mFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); 167 // This is the PdfRenderer we use to render the PDF. 168 if (mFileDescriptor != null) { 169 mPdfRenderer = new PdfRenderer(mFileDescriptor); 170 } 171 } 172 173 /** 174 * Closes the {@link android.graphics.pdf.PdfRenderer} and related resources. 175 * 176 * @throws java.io.IOException When the PDF file cannot be closed. 177 */ 178 private void closeRenderer() throws IOException { 179 if (null != mCurrentPage) { 180 mCurrentPage.close(); 181 } 182 mPdfRenderer.close(); 183 mFileDescriptor.close(); 184 } 185 186 /** 187 * Shows the specified page of PDF to the screen. 188 * 189 * @param index The page index. 190 */ 191 private void showPage(int index) { 192 if (mPdfRenderer.getPageCount() <= index) { 193 return; 194 } 195 // Make sure to close the current page before opening another one. 196 if (null != mCurrentPage) { 197 mCurrentPage.close(); 198 } 199 // Use `openPage` to open a specific page in PDF. 200 mCurrentPage = mPdfRenderer.openPage(index); 201 // Important: the destination bitmap must be ARGB (not RGB). 202 Bitmap bitmap = Bitmap.createBitmap(mCurrentPage.getWidth(), mCurrentPage.getHeight(), 203 Bitmap.Config.ARGB_8888); 204 // Here, we render the page onto the Bitmap. 205 // To render a portion of the page, use the second and third parameter. Pass nulls to get 206 // the default result. 207 // Pass either RENDER_MODE_FOR_DISPLAY or RENDER_MODE_FOR_PRINT for the last parameter. 208 mCurrentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY); 209 // We are ready to show the Bitmap to user. 210 mImageView.setImageBitmap(bitmap); 211 updateUi(); 212 } 213 214 /** 215 * Updates the state of 2 control buttons in response to the current page index. 216 */ 217 private void updateUi() { 218 int index = mCurrentPage.getIndex(); 219 int pageCount = mPdfRenderer.getPageCount(); 220 mButtonPrevious.setEnabled(0 != index); 221 mButtonNext.setEnabled(index + 1 < pageCount); 222 getActivity().setTitle(getString(R.string.app_name_with_index, index + 1, pageCount)); 223 } 224 225 /** 226 * Gets the number of pages in the PDF. This method is marked as public for testing. 227 * 228 * @return The number of pages. 229 */ 230 public int getPageCount() { 231 return mPdfRenderer.getPageCount(); 232 } 233 234 @Override 235 public void onClick(View view) { 236 switch (view.getId()) { 237 case R.id.previous: { 238 // Move to the previous page 239 showPage(mCurrentPage.getIndex() - 1); 240 break; 241 } 242 case R.id.next: { 243 // Move to the next page 244 showPage(mCurrentPage.getIndex() + 1); 245 break; 246 } 247 } 248 } 249 250 } 251