Pdf.js: Blurry Rendering on High DPI Display

Created on 29 Jan 2019  路  17Comments  路  Source: mozilla/pdf.js

PDFs viewed using the Firefox built-in PDF viewer render blurry on Mac. It appears that the PDF viewer is not rendering at Retina-level resolution.

Configuration:

  • Web browser and its version: Firefox Nightly 67.0a1 (Also tested previously with with Firefox Beta 65 and 66).
  • Operating system and its version: macOS Mojave 10.14.2

Steps to reproduce the problem:

  1. Open any PDF in the built-in PDF viewer using Firefox for Mac with a high DPI display. The PDF will appear blurry. Opening the same PDF in either Chrome or Safari will result in a crisp, clear image.
    Example

What is the expected behavior? Crisp rendering

What went wrong? Blurry rendering

1-viewer 3-upstream

Most helpful comment

I was able to improve image quality by increasing scale and then reducing the canvas style size by the scale. I believe this is effectively increasing the DPI per canvas dimensions? Also kinda a hack, is there a better way to do this?

        var scales = { 1: 3.2, 2: 4 },
            defaultScale = 3,
            scale = scales[window.devicePixelRatio] || defaultScale;

        var viewport = page.getViewport({ scale: scale });
        canvas.height = viewport.height;
        canvas.width = viewport.width;

        var displayWidth = 1.5;
        canvas.style.width = `${(viewport.width * displayWidth) / scale}px`;
        canvas.style.height = `${(viewport.height * displayWidth) / scale}px`;

increasing the scale alone as @apoorv-mishra suggested. I would end up with the pdf taking up just the top left quarter of the page.

|X |
|  |
+--+

All 17 comments

Another example here. Just view in Firefox on a Retina display Mac.

Currently on Macbook Air 13-inch 2018 running OS X 10.14.2 with Firefox Nightly 67.0a1.

pdf.js uses a canvas for rendering, which renders at "device-independent" resolution. On high dpi screens, this is lower than the actual "native device-pixel resolution", so the canvas bitmap is upscaled to the display.

I found some links on how to draw a canvas at "device-pixel resolution". I cannot confirm these work, or that they're pixel-perfect with no rounding errors or linear-interpolation blurring.

https://gist.github.com/callumlocke/cc258a193839691f60dd
https://stackoverflow.com/questions/19142993/how-draw-in-high-resolution-to-canvas-on-chrome-and-why-if-devicepixelratio

Do we need to update examples for resolving this or change a part of the code so that we don't need to update examples? If we follow the latter approach, we'll have to handle this here. What do you think @Snuffleupagus ?

Do we need to update examples for resolving this or change a part of the code so that we don't need to update examples?

I really don't understand this question, since it's clearly already supported in PDF.js courtesy of https://github.com/mozilla/pdf.js/blob/06b253d6092e14a118f9d5b23fa0652498c68a96/web/ui_utils.js#L95

Besides, this issue is (most likely) a duplicate of https://bugzilla.mozilla.org/show_bug.cgi?id=1537955, in which case it cannot really be fixed here anyway. Closing as duplicate of the upstream issue?

Of course it is, but not if one tries the examples here on High DPI displays. For that to work, we'd have add something like

canvas.style.width = viewport.width
canvas.style.height = viewport.height

canvas.width = viewport.width * window.devicePixelRatio;
canvas.height = viewport.height * window.devicePixelRatio;

Right?

The image linked in https://github.com/mozilla/pdf.js/issues/10509#issue-404449549 clearly shows the default viewer, hence my remarks in https://github.com/mozilla/pdf.js/issues/10509#issuecomment-500881064 still stands.

https://mozilla.github.io/pdf.js/examples/ is purposely a very basic example, and when using something like the "viewer components" examples there's already support for HiDPI built-in (again see https://github.com/mozilla/pdf.js/issues/10509#issuecomment-500881064).

I was able to improve image quality by increasing scale and then reducing the canvas style size by the scale. I believe this is effectively increasing the DPI per canvas dimensions? Also kinda a hack, is there a better way to do this?

        var scales = { 1: 3.2, 2: 4 },
            defaultScale = 3,
            scale = scales[window.devicePixelRatio] || defaultScale;

        var viewport = page.getViewport({ scale: scale });
        canvas.height = viewport.height;
        canvas.width = viewport.width;

        var displayWidth = 1.5;
        canvas.style.width = `${(viewport.width * displayWidth) / scale}px`;
        canvas.style.height = `${(viewport.height * displayWidth) / scale}px`;

increasing the scale alone as @apoorv-mishra suggested. I would end up with the pdf taking up just the top left quarter of the page.

|X |
|  |
+--+

Assuming that the problem described in https://github.com/mozilla/pdf.js/issues/10509#issue-404449549 was related to "resistFingerprinting", then this issue should now have been fixed (in the latest Nightly) courtesy of https://bugzilla.mozilla.org/show_bug.cgi?id=1537955.

Assuming that the problem described in #10509 (comment) was related to "resistFingerprinting", then this issue _should_ now have been fixed (in the latest Nightly) courtesy of https://bugzilla.mozilla.org/show_bug.cgi?id=1537955.

Unfortunately, it is _not_ fixed in the latest Nightly

Unfortunately, it is _not_ fixed in the latest Nightly

Just to make absolutely sure, can you please enter about:buildconfig in the address bar and copy-and-paste the line that reads "Built from ..." near the top.

This is now fixed on the latest Nightly!
Build config: https://hg.mozilla.org/mozilla-central/rev/36c3240e5cafd7b57146bab3b177bfa47f42bcfa

Hi, I'm still experiencing this issue on latest Firefox with this user.js:

user_pref("privacy.firstparty.isolate", true);
user_pref("privacy.resistFingerprinting", true);

Version: Firefox Developer Edition 71.0b10, build rev 992520c31cde6750358c5d5c3a610f77ebadd404

OS: Debian 10

The problem appears only when the two above settings are set. With the default config rendering is crisp.

@ChrisCPO I had the exact same problem with the PDF filling only the upper left quarter. I think I found the "clean" solution here

It's an extension of @apoorv-mishra solution:

const devicePixelRatio = window.devicePixelRatio || 1;

canvas.style.width = viewport.width;
canvas.style.height = viewport.height;

canvas.width = viewport.width * devicePixelRatio;
canvas.height = viewport.height * devicePixelRatio;

// [sx, 0, 0, sy, 0, 0]
const transform = [ devicePixelRatio, 0 , 0, devicePixelRatio, 0, 0];

const renderContext = {
  canvasContext: canvas.getContext("2d");
  viewport: viewport,
  transform: transform
}

@Franziskus1988 I can confirm that this solution does indeed work (well) for initial rendering. Subsequent zooming in also works well. However, when zooming out to say 50% and then back to 400% on a hi-dpi PDF the blurriness remains. Just a note for anyone else following this thread. Thanks for the code though, it has saved me a lot of headaches so far.

@Jon-Murray I didn't come across this problem, because we are limiting the zooming in our application. It suprises me a little bit that it fails on zoom.
In the original implementation here they are setting a maxScale. Maybe you could try to use this more sophisticated code from the original pdf.js viewer.

The result from code by @ChrisCPO looks much better than that by @Franziskus1988

It is better to set the scale based on the display resolution andzoom level.

Here is a link on How to detect page zoom level in all modern browsers? https://stackoverflow.com/questions/1713771/how-to-detect-page-zoom-level-in-all-modern-browsers

Was this page helpful?
0 / 5 - 0 ratings

Related issues

liuzhen2008 picture liuzhen2008  路  4Comments

PeterNerlich picture PeterNerlich  路  3Comments

azetutu picture azetutu  路  4Comments

SehyunPark picture SehyunPark  路  3Comments

hp011235 picture hp011235  路  4Comments