25

I'm using PdfRendererabove api 21 to display pdf in my app and I noticed that the quality of pages is very poor. I followed also google sample to use PdfRenderer and this is how I create Bitmap for page:

//mCurrentPage is a PdfRenderer.Page and mImageView is an ImageView
Bitmap bitmap = Bitmap.createBitmap(mCurrentPage.getWidth(), 
                    mCurrentPage.getHeight(),
                    Bitmap.Config.ARGB_8888);
mCurrentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
mImageView.setImageBitmap(bitmap);

I used ARGB_8888 because as far as I know, it's the best quality to display bitmaps. Am i doing something wrong?

EDIT

This is the huge difference between PdfRenderer class and a classic Pdf reader:

enter image description here enter image description here

Giorgio Antonioli
  • 15,771
  • 10
  • 45
  • 70
  • After you zoom in/out or switch in/out of focus, does the quality of the pdf change? – Sipty Aug 28 '15 at 14:57
  • 1
    @Sipty No, it doesn't. – Giorgio Antonioli Aug 28 '15 at 14:59
  • It's scary how limited the class feels! I would recommend applying AA, alongside with a few other methods, to up the quality of the bitmap. If that doesn't work, the next best step might be for you to opt in for an external lib. My guess is that the Android guys are not using subpixel rendering and/or an issue with the specific fonts. There are some fantastic libs out there, that would offer you a plethora of extra useful options. – Sipty Aug 28 '15 at 15:13
  • 1
    @Sipty I'm actually using an external lib below api 21. But, regarding to the fact that Android sdk provides this class above api 21, I've tried to implement it – Giorgio Antonioli Aug 28 '15 at 15:17
  • Oh, wow, the difference is... Dang. Can you please link the lib you're using, for future reference? – Sipty Aug 28 '15 at 16:23
  • 1
    @Sipty https://github.com/JoanZapata/android-pdfview but the screenshot is from Adobe Reader – Giorgio Antonioli Aug 28 '15 at 17:55

3 Answers3

58

ARGB_8888` is for color quality only but the printing/displaying quality is related to the resolution (how much dots per inch you have when displaying on screen).

For example, if you have 400 DPI screen (400 Dots Per Inch) and want to display PDF with this quality then you should render the bitmap via Bitmap.createBitmap() that takes pixels as its sizes:

Bitmap bitmap = Bitmap.createBitmap(
    getResources().getDisplayMetrics().densityDpi * mCurrentPage.getWidth() / 72,                        
    getResources().getDisplayMetrics().densityDpi * mCurrentPage.getHeight() / 72,
    Bitmap.Config.ARGB_8888
);

where:

  1. getResources().getDisplayMetrics().densityDpi is the target DPI resolution
  2. mCurrentPage.getWidth() returns width in Postscript points, where each pt is 1/72 inch.
  3. 72 (DPI) is the default PDF resolution.

Hence, diving #2 by 72 we get inches and multiplying by DPI we get pixels. In other words to match the quality of the printing device of the display you should increase the size of the image rendered as default PDF resolution is 72 DPI. Please also check this post?

soshial
  • 5,906
  • 6
  • 32
  • 40
Eugene
  • 2,820
  • 19
  • 24
  • 1
    I understood, thanks for your answer, it helped a lot! – Giorgio Antonioli Sep 01 '15 at 08:57
  • 1
    Eugene M - Great answer. I have a question. Now that I increased the quality the app is getting very slow. I guess it's running out of memory. would you know a solution ? thanks – Thiago May 03 '16 at 09:50
  • @Joolah the higher quality leads to higher image size and higher amount of memory. I would play to decrease the DPI (dots per inch) when rendering full image – Eugene May 04 '16 at 15:06
  • 1
    @Joolah you may calculate how much bytes Bitmap uses by caclulating bitmap.getWidth()*bitmap.getHeight() * 4 (for ARGB every pixel uses 32 bytes) – Eugene May 04 '16 at 15:13
  • My understanding is getWidth and getHeight return the dimensions in points while createBitmap takes dimensions in pixels, so the conversion you have makes sense but I end up with a scaled output whereas when I use the bitmap size without conversion (as in the original question) I get the correct size .. Doesn't make sense but I'm not sure what is going wrong? Do I need to scale back down? – Nonos May 27 '16 at 14:14
  • @Nonos yes, you have points for width/height of the PDF file's page. The difference is with DPI, dots per inch: on 72 dpi screen you may draw a 1 inch line with 72 pixels. But on 400 dpi screen you need 400 points (pixels) instead. The PDF default sizes are based on 72 DPI but you may render it to 400 dpi too with proper pdf renderer. You will get smoother lines, edges etc but you will get more pixels. Most picture formats store DPI setting so image viewers may adjust picture the target screen. – Eugene Jun 07 '16 at 08:29
  • 3
    Beware of using this densityDpi / 72 - after I did it, it was causing OutOfMemory errors on newer high-DPI devices. So now, I simply multiplied my height and width by 2 to get a crisper image and it looks pretty good. – Anthony Chuinard Nov 11 '16 at 21:10
  • 4
    This would render a bitmap large enough for full resolution on a device the same size as the PDF page. Since most devices are much smaller, this is way more memory than needed. For an 8.5"x11" PDF on my 420 DPI Nexus 5x this generates a 69 MB bitmap. Using getResources().getDisplayMetrics().widthPixels/heightPixels instead (or scaling as per blae's answer below) gives my a 6.5 MB bitmap, less than 10% the memory usage, that is the best bitmap my device can render anyway. The only reason for higher is if one is zooming on a particular section. – Oded Dec 15 '16 at 07:10
  • 1
    On some devices with large screens but medium/low DPI (like tablets), the rendered bitmap might be not big enough to fill up the whole screen. For example, on Nexus 9 (1536x2048 and 320 dpi) this code generates bitmap that has resolution of only 1133x1634. Increasing these sizes proportionally to exact needed pixels would be a wiser solution. – soshial Aug 24 '17 at 19:41
  • i cant resolve this issue by using this code please help me out anyone – Mohammad Zeeshan Nov 04 '21 at 08:24
7

I am a bit late. The answer marked did not help me, because the bitmap got to big, and thus could not be rendered. I found the original post because I had the exact same problem, maybe this answer will help others in my situation.

I searched around to see if anyone had a neat solution to scaling the bitmap, while keeping the ratio. I came across this site:

https://guides.codepath.com/android/Working-with-the-ImageView#scaling-a-bitmap

It is not complicated stuff, really. I only needed two additional lines of code:

int height = DeviceDimensionsHelper.getDisplayHeight(this);
Bitmap scaledBitmap = BitmapUtil.scaleToFitHeight(bitmap, height);

I added similar helper/util classes as the site suggests(so in practice, it is more than two lines ;) ). The height comes from

context.getResources().getDisplayMetrics().widthPixels;

The scaling simply divides the device height with the original bitmap height to get a factor, which is in turn multiplied with the original bitmap width to find the scaled width.

This works perfectly on my Nexus 5. I have not been able to test this on multiple devices, but it seems like a solid solution.

stianak
  • 83
  • 1
  • 5
1

As with the PdfRenderer you'll be displaying PDF pages in an ImageView, it's easiest to simply create a bitmap of a max size of your ImageView, with some maths added to preserve the PDF's proportions. Since PDF's are generally made for print, they'll always be of higher resolution than your screen, so you're not risking getting a pixelated image. To be sure the ImageView has it's measuredWidth and measuredHeight assigned, use ImageView::post() in your code (excuse my Kotlin):

    pdfPageImageView.post {
        val viewWidth = pdfPageImageView.measuredWidth
        val viewHeight = pdfPageImageView.measuredHeight
        val pageWidth = page.width
        val pageHeight = page.height
        val pageRatio = 1.0 * pageWidth / pageHeight
        val viewRatio = 1.0 * viewWidth / viewHeight
        val bitmapWidth: Int
        val bitmapHeight: Int
        if (pageRatio > viewRatio) {
            bitmapWidth = viewWidth
            bitmapHeight = (viewWidth / pageRatio).toInt()
        } else {
            bitmapHeight = viewHeight
            bitmapWidth = (viewHeight * pageRatio).toInt()
        }
        val bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888)
        page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
        pdfPageImageView.setImageBitmap(bitmap)
    }
javaxian
  • 1,815
  • 1
  • 21
  • 26