eclipse-platform / eclipse.platform.swt

Eclipse SWT
https://www.eclipse.org/swt/
Eclipse Public License 2.0
111 stars 127 forks source link

Incorrect font height #1193

Open PatrickTasse opened 4 months ago

PatrickTasse commented 4 months ago

Describe the bug

The font is not the expected height in pixels, and FontMetrics.getHeight() is wrong according to Javadoc.

To Reproduce

for (int h = 1; h <= 100; h++) { Font font = new Font(getDisplay(), new FontData("Arial", h, SWT.NORMAL)); gc.setFont(font); FontData fd = font.getFontData()[0]; System.out.println("fontData.getHeight()="+fd.getHeight()+"pts gc.getFontMetrics().getHeight()="+gc.getFontMetrics().getHeight()+"pts gc.textExtent(\"\").y="+gc.textExtent("").y+"px gc.getDevice().getDPI().y="+gc.getDevice().getDPI().y+"dpi"); font.dispose(); }

Expected behavior

I would expect that for a font height 'h' in points, the font height in pixels should be: h pts x DPI px/in / PPI pts/in where DPI is 96 according to Device.getDPI() and PPI is 72 pts/in so a ratio of 4/3, but the snippet above outputs: fontData.getHeight()=24pts gc.getFontMetrics().getHeight()=36pts gc.textExtent("").y=36px gc.getDevice().getDPI().y=96dpi fontData.getHeight()=100pts gc.getFontMetrics().getHeight()=150pts gc.textExtent("").y=150px gc.getDevice().getDPI().y=96dpi it appears that the ratio of px/pt is 3/2?

Also, FontMetrics.getHeight()'s Javadoc states: "Returns the height of the font described by the receiver, measured in points." but the returned value is obviously in pixels. I'm not sure if the value or the Javadoc is wrong?

If there is some other external scaling factor that is missing, is there a way to determine it programmatically? I would expect that it would be considered in Device.getDPI().

The use case I have is to set the font height (in points) to get an exact desired height in pixels.

Environment:

  1. Select the platform(s) on which the behavior is seen:

      • [ ] All OS
      • [ ] Windows
      • [x] Linux
      • [ ] macOS
  2. Additional OS info (e.g. OS version, Linux Desktop, etc)

Linux Ubuntu

  1. JRE/JDK version

Version since

Tested on Eclipse 4.31 SWT 3.125

iloveeclipse commented 4 months ago

Could you provide a simple test case that shows the problem, ideally as an SWT test? Could you please also specify your screen resolution, OS, Desktop, X11 or Wayland, SWT and GTK3 versions?

PatrickTasse commented 4 months ago

1920x1200, Linux Ubuntu, Gnome Shell, X11, SWT 3.125.0.v20240227-1638, GTK 3.24.33

image

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class SnippetFont {

    public static void main(String[] args) {
        Display display = new Display();
        String gtkVersion = System.getProperty("org.eclipse.swt.internal.gtk.version");
        Shell shell = new Shell(display);
        shell.setText("gtk version:" + gtkVersion + " display bounds:" + display.getBounds() + " DPI:" + display.getDPI().y);

        shell.setLayout(new FillLayout());

        shell.addPaintListener(e -> {
            GC gc = e.gc;
            gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
            gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));
            int x = 3;
            int y = 3;
            final int PPI = 72;
            final int DPI = display.getDPI().y;
            for (int pixels = 2; pixels <= 100; pixels++) {
                int points = pixels * PPI / DPI;
                Font font = new Font(display, new FontData("Arial", points, SWT.NORMAL ) );
                gc.setFont(font);
                FontData fd = gc.getFont().getFontData()[0];
                FontMetrics fm = gc.getFontMetrics();
                String text = String.format("desired pixels:%d calculated points:%d*%d/%d=%d fontData.height:%d fontMetrics.height:%d",
                        pixels, pixels, PPI, DPI, points, fd.getHeight(), fm.getHeight());
                Point textExtent = gc.textExtent(text);
                gc.drawText(text, x, y, false);
                gc.drawText("textExtent.y:" + textExtent.y, x + textExtent.x + 10, y, true);
                font.dispose();
                y += textExtent.y + 3;
            }
        });

        shell.setBounds(display.getBounds());
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
}
PatrickTasse commented 4 months ago

If I make the points = 2/3 * pixels, I get the desired result but I can't explain the math :(

image

SyntevoAlex commented 4 months ago

Try changing screen's DPI to 200% in your Linux settings and see what the new "magic" constant would be.

PatrickTasse commented 3 months ago

Try changing screen's DPI to 200% in your Linux settings and see what the new "magic" constant would be.

I'm not sure if this is what you mean, but I set Scale to 200% and programmatically the numbers are the same except that the actual display is zoomed 2x.

image