Skip to content

Instantly share code, notes, and snippets.

@stefanplindner
Created May 17, 2016 12:06
Show Gist options
  • Save stefanplindner/ef96abb00204b8e6e0f378cfe026a6d7 to your computer and use it in GitHub Desktop.
Save stefanplindner/ef96abb00204b8e6e0f378cfe026a6d7 to your computer and use it in GitHub Desktop.
PDFRenderer for subsampling-scale-image-view
/**
* Decodes and renders a {@link PdfRenderer.Page} into a {@link Bitmap}
* Created by Lindner Stefan on 03.05.16.
*/
@SuppressLint("NewApi")
public class PDFDecoder implements ImageDecoder {
private float scale;
/**
* the current pdf site
*/
private int position;
/**
* the pdf file to render
*/
private File file;
/**
* basic constructor for PDFDecoder.
* @param scale the scale to get from {@link Point} to Pixel
*/
public PDFDecoder(int position, File file, float scale) {
this.file = file;
this.scale = scale;
this.position = position;
}
/**
* Creates a {@link Bitmap}in the correct size and renders the {@link PdfRenderer.Page} into it.
* @param context not used
* @param uri not used
* @return a bitmap, containing the pdf page
* @throws Exception, if rendering fails
*/
@Override public Bitmap decode(Context context, Uri uri) throws Exception {
ParcelFileDescriptor descriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);;
PdfRenderer renderer = new PdfRenderer(descriptor);
final PdfRenderer.Page page = renderer.openPage(position);
Bitmap bitmap = Bitmap.createBitmap((int)(page.getWidth()*scale+0.5), (int)(page.getHeight()*scale+0.5f),
Bitmap.Config.ARGB_8888);
page.render(bitmap,null,null,PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
page.close();
renderer.close();
descriptor.close();
return bitmap;
}
}
/**
* Created by Lindner Stefan on 05.05.2016
*/
@SuppressLint("NewApi") public class PDFPagerAdapter extends PagerAdapter {
/**
* context for the view
*/
private Context context;
/**
* pdf file to show
*/
private File file;
/**
* file descriptor of the PDF
*/
private ParcelFileDescriptor mFileDescriptor;
/**
* this scale sets the size of the {@link PdfRenderer.Page} in the {@link
* PDFRegionDecoder}.
* Since it rescales the picture, it also sets the possible zoom level.
*/
private float scale;
/**
* this renderer is only used to count the pages
*/
private PdfRenderer renderer;
/**
* @param file the pdf file
*/
public PDFPagerAdapter(Context context, File file) {
super();
this.context = context;
this.file = file;
this.scale = 8;
try {
mFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
renderer = new PdfRenderer(mFileDescriptor);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Instantiates an item. Therefor a {@link SubsamplingScaleImageView} with special decoders is
* initialized and rendered.
*
* @param container isn't used here
* @param position the current pdf page position
*/
public Object instantiateItem(ViewGroup container, int position) {
SubsamplingScaleImageView imageView = new SubsamplingScaleImageView(context);
// the smaller this number, the smaller the chance to get a "outOfMemoryException"
// still, values lower than 100 really do affect the quality of the pdf picture
int minimumTileDpi = 100;
imageView.setMinimumTileDpi(minimumTileDpi);
imageView.setBitmapDecoderFactory(() -> new PDFDecoder(position, file, scale));
imageView.setRegionDecoderFactory(() -> new PDFRegionDecoder(position, file, scale));
ImageSource source = ImageSource.uri(file.getAbsolutePath());
imageView.setImage(source);
container.addView(imageView);
return imageView;
}
/**
* gets the pdf site count
*
* @return pdf site count
*/
public int getCount() {
return renderer.getPageCount();
}
@Override public void destroyItem(ViewGroup container, int position, Object view) {
container.removeView((View) view);
}
@Override public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
/**
* Decodes and renders a given rect out of a {@link PdfRenderer.Page} into a {@link Bitmap}
* Created by Lindner Stefan on 03.05.16.
*/
@SuppressLint("NewApi") public class PDFRegionDecoder implements ImageRegionDecoder {
/**
* the page that will be rendered to a bitmap.
*/
private PdfRenderer renderer;
/**
* scale: defined in {@link DocumentActivity}
*/
private float scale;
/**
* the current page position in the pdf
*/
private int position;
/**
* the pdf page
*/
private PdfRenderer.Page page;
/**
* the file descriptor
*/
private ParcelFileDescriptor descriptor;
/**
* the pdf file
*/
private File file;
/**
* basic constructor for PDFDecoder.
*
* @param scale the scale to get from {@link Point} to Pixel
*/
public PDFRegionDecoder(int position,File file, float scale) {
this.file = file;
this.scale = scale;
this.position = position;
}
/**
* Initializes the region decoder. This method initializes
* @param context not used here
* @param uri not used here (file is already loaded)
* @return the rescaled point
* @throws Exception
*/
@Override public Point init(Context context, Uri uri) throws Exception {
this.descriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
this.renderer = new PdfRenderer(descriptor);
page = renderer.openPage(position);
return new Point((int) ((page.getWidth() * scale + 0.5f)),
(int) ((page.getHeight() * scale + 0.5f)));
}
/**
* Creates a {@link Bitmap} in the correct size and renders the region defined by rect of the
* {@link PdfRenderer.Page} into it.
*
* @param rect the rect of the {@link PdfRenderer.Page} to be rendered to the bitmap
* @param sampleSize the sample size
* @return a bitmap containing the rendered rect of the page
*/
@Override public Bitmap decodeRegion(Rect rect, int sampleSize) {
int bitmapWidth = rect.width() / sampleSize;
int bitmapHeight = rect.height() / sampleSize;
Bitmap bitmap = Bitmap.createBitmap(bitmapWidth , bitmapHeight,
Bitmap.Config.ARGB_8888);
Matrix matrix = new Matrix();
matrix.setScale(scale/sampleSize,scale/sampleSize);
matrix.postTranslate(-rect.left/sampleSize, -rect.top/sampleSize);
page.render(bitmap, null, matrix, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
return bitmap;
}
@Override public boolean isReady() {
return true;
}
/**
* close everything
*/
@Override public void recycle() {
page.close();
renderer.close();
try {
descriptor.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@borgeser
Copy link

To be more generic and close to the original API, I prefer to work with Uri instead of File in the renderers.

So I replaced this line:
this.descriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)

by this line:
this.descriptor = context.contentResolver.openFileDescriptor(uri, "r")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment