pcorless / icepdf

PDF Rendering and Viewing API in Java
Apache License 2.0
81 stars 21 forks source link

Poor rendering if OS (Windows) scales to 125% due to monitor resolution #311

Open folkfreund opened 1 year ago

folkfreund commented 1 year ago

Thanks for this really great piece of software!

I am using ICEpdf on a Windows 10 machine. My PDFs are rendered perfectly, as long as the monitor scaling is set to 100%. When I move the viewer window to my other monitor, which has scaling set to 125% (recommended), then the PDF seems to be scaled up and fonts get blurred. This happens regardless of the current scaling factor in the viewer.

Is there any way to avoid this?

pcorless commented 1 year ago

Thanks, always nice to get some feed back, even more so if its positive.

This is an interesting problem. It's not specific the the library but from my readings is more of a JDK issue. Could you try the following systems properties (https://news.kynosarges.org/2019/03/24/swing-high-dpi-properties/)?

-Dsun.java2d.uiScale.enabled=false -Dsun.java2d.win.uiScaleX=1.25 -Dsun.java2d.win.uiScaleY=1.25

folkfreund commented 1 year ago

Hi Patrick, thanks for your quick response. sure I'll try these properties. But I do not think, this will resolve the problem. I can't see how these properties could do the job on my dual monitor setup, one 100%, the other 125%. I see, that the paintComponent function is called when I move the window from one monitor to the other. The graphics2D context gives me different scaling values (1.0 and 1.25) on the different displays.

Also I found an interesting statement here: https://github.com/JFormDesigner/FlatLaf/blob/main/flatlaf-core/src/main/java/com/formdev/flatlaf/util/UIScale.java (See the comment at the top of the class l. 48-55)

Maybe the PDF could be prepared with higher resolution if necessary and drawn with scale 1.0?

pcorless commented 1 year ago

The UIScale class you posted does indeed have some very interesting information in it.

The Viewer RI AbstractPageViewComponent uses a BufferedImage to cache the latest render of the PDF content. There is a bunch of logic that copies cached content and tries to limit the size of clip when updating for scrolling and scaling. The buffer is then painted using java.awt.Graphics2D and will pick up on the JRE applied scaling. This probably explains the blurring you are seeing.

I don't have the hardware to test this properly any chance you could pull https://github.com/pcorless/icepdf/tree/GH-311.fraction.scaling.compensation and give the theory a try?

folkfreund commented 1 year ago

Yes, sure I'll give this a try :-) You don't need special hardware, you can always change the scaling of your display (as long as you are on windows).

folkfreund commented 1 year ago

It seems, your idea does not change anything. :-(

If you add double scale = g2d.getTransform().getScaleX() scale contains 1.25, even after scale was set to 1.0 as in your try. It seems, the call to scale just modifies the existing transformation.

I also tried to replace the AffineTransform with identity transform (I know, you should NEVER do that...). Then the PDF content is not scaled any more and thus look fine, but the 'paper' is too big. It seems, it was painted before already.

Maybe we could do something like

double scale = g2d.getTransform().getScaleX();
if (scale != 1.0) {
  g2d.scale(1.0/scale, 1.0/scale);
}

and something similar where the paper boundaries are painted?

folkfreund commented 1 year ago

I experimented a bit and now I'm able to prevent the scaling. The page layouts (e.g. OnePageViewLayout) do not consider the smaller scale of the pages yet (sure this can be fixed, too).

But I'm not sure if this is what we really want. Before I continue please let me know what you think:

What is the real meaning of a scale 100% shown by the UI? Should the page appear on the screen in the same size as printed on paper? Then we are not allowed to just avoid the upscaling on a screen with smaller dots.

BTW: on my normal 100% screen a A4 page shows up with a width of 161mm (should be 210mm) It might be by coincidence, but the factor is almost 96 / 72. Could it be, that there should be an additional scaling from 72 pt/inch to 96 px/inch?

But maybe nobody cares about the real size of the picture on the screen?

pcorless commented 1 year ago

That's interesting about the page layouts but I guess that makes sense in a way. I'll give it another try and see about passing in the scale factor into the code that creates the buffer, see if I can make some headway with that angle. I haven't been able to reproduce this on my Linux system yet but will keep trying. Any chance you could post a screen shot of the blurred output?

Papers size has come up over the years but I could probably count then on one hand. It's pretty much just like you pointed out 72 pt/inch to 96 px/inch. The PDF spec states one thing and Java2D does another. I don't think this would be too difficult to fix but I think there are some consideration with Apple and Windows but I can't remember if Java2d automatically corrects for that. More reading.

folkfreund commented 1 year ago

Here are some screenshots. First blurred output on my 125% display, created with icepdf master blurred_125 Then the same on my 100% display, window just moved to the other screen perfect_100

And now my current state with my fixes. PDF looks fine, but the layout/background is too big. Note the vertical scrollbar and off center horizontally fixed-bad-layout1 Last an example with TwoColumnPageViewLayout fixed-bad-layout2

folkfreund commented 1 year ago

Yes, maybe it is worth a try to directly create the image with a modified scaling factor. The bad thing is, that we cannot get the scaling factor before we get the Graphics2D in paint function...

Would you like me to create a pull request with my changes? I'm not very familiar with GitHub. Do I need to do this from my repo at GitHub? Up to now I just cloned your repo locally.

folkfreund commented 1 year ago

Another note: up to now I just looked at the PDF content itself. No idea, if all the tools need scaling, too?

pcorless commented 1 year ago

I was poking around tonight and there really isn't much info on this issue. There is a thread on Intellij support that discusses the issue with regards to the the IDE. They seem to make the distinction of app scaling vs the jre scaling.

Have you tried a simple configuration of: -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1 I'm curious what this produces vs. -Dsun.java2d.uiScale.enabled=false

From you screen shots this definitely looks like the buffer being scaled by 1.25. In your cloned repo, you should be able to create new branch with your work committed to it. Then create a pull request against pcorless:master.

folkfreund commented 1 year ago

-Dsun.java2d.uiScale.enabled=false does not work at all on 125% display, it just does not redraw the full window on resize uiScale enabled=false

-Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1 These turn off the scaling for the whole application. This avoids blurred display and might be used as workaround in some cases. But it is not, what I want for my app, where I'm integrating the viewer as a small part of the program. fixed_scaling_1

I'll create the pull request later.

pcorless commented 1 year ago

Thanks for the info, I'll continue to work on the buffer angle. Found a pretty good lead here, https://stackoverflow.com/questions/61558678/how-do-i-double-buffer-in-java-swing-on-a-retina-display-without-losing-the-high . Fairly sure I can incorporate the approach.

pcorless commented 1 year ago

This is still on the top of my todo list. I'm currently trying to wrap up a bunch of work on redacting text.

SirFrancix commented 2 weeks ago

The same problem occurs when an image of a page is to be created via getPageImage and the physical screen resolution is not 100% and it happens especially when truetype fonts are embedded in the PDF.

pcorless commented 2 weeks ago

Thanks for extra information.