Skip to content

Conversation

@laeubi
Copy link
Contributor

@laeubi laeubi commented Feb 6, 2026

This is how it looks like

grafik

This is currently waiting for

because a PDFDocument is not a Device it does not have a getDPI() but needs one if one wants to translate between page size and actual drawing points.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Test Results

  176 files  ±0    176 suites  ±0   28m 24s ⏱️ - 1m 24s
4 684 tests ±0  4 662 ✅ ±0  22 💤 ±0  0 ❌ ±0 
  485 runs  ±0    479 ✅ ±0   6 💤 ±0  0 ❌ ±0 

Results for commit fc466e9. ± Comparison against base commit 9112e41.

♻️ This comment has been updated with latest results.

@laeubi laeubi force-pushed the snippet_drawing_device branch from 36a6788 to fc466e9 Compare February 6, 2026 07:02
@laeubi
Copy link
Contributor Author

laeubi commented Feb 6, 2026

After the merge, the results are now like this in Linux:

grafik

please notice that I have to set the Zoom to 50% in both cases (printed to a PDF and exported directly as PDF) to get the same size as in the Control case. Also printing to a real printers as paper gives correct results of about 10cm.

So even though the DPI is reported as being 96 it seem to be really more like the half of it. If I measure the true size with a ruler then I get 6cm for the on-screen and 12cm for the PDF case so something seem to add a factor of two here.

@laeubi
Copy link
Contributor Author

laeubi commented Feb 6, 2026

A similar effect on windows, just that the factor between PDF and Screen is around 45% (pdf shown at 50% zoom) and printing to printer gives ultra small fonts (so not really visible at all on screenshot)
grafik

@laeubi
Copy link
Contributor Author

laeubi commented Feb 6, 2026

@Phillipus would be interesting to see the behavior on Mac!

Beside this Snippets will not be tested by the build so we can merge... (Jenkins hang again).

@laeubi laeubi merged commit 8dcdeab into eclipse-platform:master Feb 6, 2026
16 of 18 checks passed
@laeubi
Copy link
Contributor Author

laeubi commented Feb 6, 2026

DPI Reporting Issue on Linux

Problem

When drawing physical measurements on screen (e.g., a 10cm box), there's a significant mismatch between expected and actual size on Linux/GTK. The reported DPI (96) doesn't match the physical DPI (~162), causing drawings to appear ~1.7x smaller than intended.

Observed Behavior

Metric Value
Display.getDPI() 96
gdk_screen_get_resolution() 96.0
Physical monitor size 600mm × 340mm
Monitor resolution 3840 × 2160
Calculated physical DPI 162.6 × 161.4
GDK scale factor 1

A 10cm box drawn at 96 DPI = 378 pixels, but physically measures only ~5.9cm on screen.

Root Cause

gdk_screen_get_resolution() returns a logical DPI setting (defaulting to 96), not the actual physical DPI. This is standard X11 behavior - 96 DPI has been the historical default regardless of actual monitor characteristics.

The physical monitor dimensions are available via gdk_monitor_get_width_mm() and gdk_monitor_get_height_mm(), but SWT currently lacks bindings for these functions.

Why Printing Works

Printers report their true physical DPI directly, so there's no mismatch between reported and actual DPI.

Proposed Solution

Add a new getPhysicalDPI() API that calculates true DPI from monitor geometry:

// New API - returns physical DPI based on monitor size in mm
Point physicalDPI = display.getPhysicalDPI();

// Existing API - unchanged, returns logical/system DPI (96)
Point logicalDPI = display.getDPI();

This approach:

  • Maintains backward compatibility (getDPI() unchanged)
  • Allows applications to opt-in to physical accuracy when needed
  • Requires adding JNI bindings for gdk_monitor_get_width_mm() and gdk_monitor_get_height_mm()

@laeubi
Copy link
Contributor Author

laeubi commented Feb 6, 2026

DPI Mismatch Analysis: Screen vs Printer vs PDF on Windows

Problem Summary

When drawing a box intended to be exactly 10 cm on screen, printer, and PDF:

Target Device Expected Size Actual Physical Size Correct?
Screen (Display) 10.0 cm ~6.1 cm (measured with ruler)
Printer (physical paper) 10.0 cm 10.0 cm
PDF (at 100% zoom in viewer) 10.0 cm 10.0 cm

The on-screen rendering is off by a factor of ~0.61 (61% of intended size).
Additionally, printer output shows ultra-small fonts because the default GC font
is not DPI-aware for printer devices.

Root Cause: Logical DPI ≠ Physical DPI

Measured Values on This System

Property Value
Windows scaling 100%
GetDeviceCaps(LOGPIXELSX) 96 (logical DPI)
Monitor resolution 1920 × 1080 px
Monitor physical size 309 mm × 174 mm (~14" diagonal)
Actual physical DPI ~158 (1920 ÷ 12.17 inches)
Per-Monitor effective DPI 96
Microsoft Print to PDF DPI 600

The Core Issue

Windows LOGPIXELSX (96 DPI at 100% scaling) is not a measurement of the
monitor's physical pixel density
. It is a user preference / scaling setting
that controls UI element sizing. The actual physical DPI of the monitor (~158 on
this system) is completely independent of this setting.

Physical DPI = Screen Resolution (pixels) / Physical Size (inches)
            = 1920 / (309mm / 25.4)
            = 1920 / 12.17
            ≈ 158 DPI

The ratio between logical and physical DPI determines the error factor:

Error factor = Logical DPI / Physical DPI = 96 / 158 ≈ 0.608

A box computed for 10 cm at 96 DPI produces 378 pixels, but 378 pixels on a
158 DPI monitor is only 378 / 158 × 25.4 = 60.8 mm6.1 cm.

Why Printers Are Correct

For printers, GetDeviceCaps(LOGPIXELSX) returns the actual hardware
resolution
(e.g., 600 DPI). The printer physically places dots at exactly that
density. So (10cm / 2.54) × 600 = 2362 pixels renders as exactly 10 cm on paper.

Why PDF Is Correct

PDFDocument uses a world transform that maps 1 PDF point = 1/72 inch:

// PDFDocument.internal_new_GC():
float scaleX = (float)(printerDpiX / POINTS_PER_INCH); // 600/72 = 8.333
OS.SetWorldTransform(handle, new float[]{scaleX, 0, 0, scaleY, 0, 0});

The PDF coordinate system is defined in terms of physical inches (72 points/inch),
and the transform correctly maps to printer pixels. PDF viewers then render the
document according to the PDF's own coordinate system.

Detailed DPI Flow in SWT (Windows)

Screen Path: Display.getDPI()

Device.getDPI()
  ├─ OS.GetDeviceCaps(hDC, LOGPIXELSX) → 96  (at 100% zoom)
  ├─ DPIUtil.mapDPIToZoom(96) → 100
  ├─ DPIUtil.getZoomForAutoscaleProperty(100) → 100
  └─ Win32DPIUtils.pixelToPointAsLocation(Point(96,96), zoom=100) → Point(96,96)

At higher Windows scaling (e.g., 150%):

Device.getDPI()
  ├─ OS.GetDeviceCaps(hDC, LOGPIXELSX) → 144  (96 × 1.5)
  ├─ DPIUtil.mapDPIToZoom(144) → 150
  ├─ DPIUtil.getZoomForAutoscaleProperty(150) → 150
  └─ Win32DPIUtils.pixelToPointAsLocation(Point(144,144), zoom=150) → Point(96,96)

getDPI() always returns 96 on Windows, regardless of the scaling level.
The autoscale system normalizes the DPI to the base value.

GC Autoscaling on Screen

When the snippet draws gc.drawRectangle(x, y, 378, 378):

GC.drawRectangle(x, y, 378, 378)
  └─ DrawRectangleOperation.apply()
       └─ Win32DPIUtils.pointToPixel(drawable, rect, getZoom())
            ├─ Display.isAutoScalable() → true
            └─ DPIUtil.pointToPixel(378, zoom)
                 └─ At 100%: 378 × 1.0 = 378 physical pixels
                 └─ At 150%: 378 × 1.5 = 567 physical pixels

Printer Path: Printer.getDPI()

Printer.getDPI()  (overrides Device.getDPI())
  ├─ OS.GetDeviceCaps(handle, LOGPIXELSX) → 600  (actual printer resolution)
  └─ Returns Point(600, 600) — NO autoscale normalization

Printer GC does not autoscale coordinates:

  • Printer.isAutoScalable()false
  • Printer.getDeviceZoom()100
  • Win32DPIUtils.pointToPixel(drawable, size, zoom) returns size unchanged

PDF Path: PDFDocument.getDPI()

PDFDocument.getDPI() → Point(72, 72)  (hardcoded, PDF coordinate system)

The world transform in internal_new_GC() handles the mapping:

User draws at 72 DPI (points) → World transform ×8.333 → 600 DPI printer pixels

The Printer "Ultra-Small Fonts" Issue

Cause

Printer.internal_new_GC() sets the default font from the printer DC:

data.font = Font.win32_new(this, OS.GetCurrentObject(handle, OS.OBJ_FONT), getDeviceZoom());

OS.GetCurrentObject(handle, OS.OBJ_FONT) returns the printer DC's default
"System" font. This is a fixed-pixel-height font (typically ~16 device units).
At 600 DPI: 16 / 600 inch = 0.68 mm ≈ 1.9 ptvirtually invisible.

Similarly for PDFDocument

Device.getSystemFont() returns OS.GetStockObject(OS.SYSTEM_FONT). When used
on a PDFDocument GC with a world transform scaling by 8.333×, the System font's
fixed pixel height becomes extremely small in the PDF coordinate space.

Impact Analysis

How the Error Factor Varies by System

Monitor Resolution Physical Size Physical DPI At 100% Scaling Error Factor
14" laptop 1920×1080 31×17 cm ~158 96 reported 0.61
24" desktop 1920×1080 53×30 cm ~92 96 reported 1.04
27" 4K 3840×2160 60×34 cm ~163 96 reported 0.59
13" MacBook-like 2560×1600 29×18 cm ~227 96 reported 0.42

The error varies wildly depending on monitor size and resolution. Only a
~24" 1080p monitor happens to have physical DPI close to 96.

At 150% Windows Scaling on This 14" Laptop

Logical DPI: 144, Physical DPI: 158
Box: 378 logical × 1.5 = 567 physical pixels
Size: 567 / 158 × 25.4 = 91.1 mm (still not 100mm, but closer)

For exact physical correctness at 158 DPI, Windows scaling would need to be
165% (158/96 × 100%), which is not a standard option.

This Is a Fundamental Platform Limitation

  • Windows: LOGPIXELSX = scaling preference, not physical measurement
  • Linux/GTK: gdk_screen_get_resolution() typically returns 96
  • macOS: Retina displays report 72 DPI (1× logical), non-Retina report 72 too

No mainstream desktop OS provides a reliable API for the true physical DPI of a
monitor. The HORZSIZE/VERTSIZE values from GetDeviceCaps() (based on monitor
EDID data) can approximate it but are often inaccurate.

Proposed Solutions

Solution 1: Calculate Physical DPI from EDID Data (Best Effort)

SWT could provide a method to estimate the physical DPI using GetDeviceCaps():

// In Display.java or a utility class:
public Point getPhysicalDPI() {
    long hDC = internal_new_GC(null);
    int horzRes = OS.GetDeviceCaps(hDC, OS.HORZRES);    // pixels
    int vertRes = OS.GetDeviceCaps(hDC, OS.VERTRES);    // pixels
    int horzSize = OS.GetDeviceCaps(hDC, OS.HORZSIZE);  // mm (from EDID)
    int vertSize = OS.GetDeviceCaps(hDC, OS.VERTSIZE);  // mm (from EDID)
    internal_dispose_GC(hDC, null);

    int physDpiX = (int) Math.round(horzRes * 25.4 / horzSize);
    int physDpiY = (int) Math.round(vertRes * 25.4 / vertSize);
    return new Point(physDpiX, physDpiY);
}

@laeubi
Copy link
Contributor Author

laeubi commented Feb 6, 2026

To summarize, my intend to really get (about) 10cm on screen is probably wrong and the snippet needs to be enhanced by checking that logical DPI is correctly transformed to physical DPI during Control#paint (and the font handling currently is flawed on windows).

@Phillipus
Copy link
Contributor

On Mac a screenshot:

Screenshot 2026-02-06 at 10 00 50

But the text controls in the exported PDF are flipped. This is already a known issue, I think.

drawing_test_snippet391.pdf

@laeubi
Copy link
Contributor Author

laeubi commented Feb 6, 2026

@Phillipus thanks yes I need to sort out some issues systematically that's why I created the snippets now as I noticed the sizing issue :-\

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants