From 27aecd626882d5565b35166e9f5761cbd17d24d0 Mon Sep 17 00:00:00 2001 From: Heiko Klare Date: Mon, 1 Jun 2026 22:08:03 +0200 Subject: [PATCH] [Win32] Fix font metrics being scaled by GC transform for fallback fonts When a font that does not exist on the platform is set on a GC and a Transform is active, GDI+ internally calls Font::GetLogFontW with the transformed Graphics to derive a fallback GDI font handle. This caused the lfHeight in the resulting LOGFONT to be scaled by the transform, so GetTextMetrics on the DC returned inflated metrics. Fix by temporarily saving and restoring the GDI+ transform around the Font::GetLogFontW call so the LOGFONT always reflects device-pixel metrics, independent of any user-applied transform. Fixes https://github.com/eclipse-platform/eclipse.platform.swt/issues/2978 Co-Authored-By: Claude Sonnet 4.6 --- .../win32/org/eclipse/swt/graphics/GC.java | 17 +++++++- .../Test_org_eclipse_swt_graphics_GC.java | 39 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java index 75889472a5..78c72cdfbe 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java @@ -680,7 +680,22 @@ static long createGdipFont(long hDC, long hFont, long graphics, long fontCollect if (outFont != null && font != 0) { long hHeap = OS.GetProcessHeap(); long pLogFont = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, LOGFONT.sizeof); - Gdip.Font_GetLogFontW(font, graphics, pLogFont); + if (graphics != 0) { + // Font_GetLogFontW applies the graphics's world transform when converting the + // font size to LOGFONT units. A non-identity user transform would cause lfHeight + // to be scaled, producing an incorrectly-sized GDI font for GetTextMetrics. + // Temporarily set identity so the LOGFONT reflects device-pixel metrics. + long savedMatrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0); + Gdip.Graphics_GetTransform(graphics, savedMatrix); + long identityMatrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0); + Gdip.Graphics_SetTransform(graphics, identityMatrix); + Gdip.Font_GetLogFontW(font, graphics, pLogFont); + Gdip.Graphics_SetTransform(graphics, savedMatrix); + Gdip.Matrix_delete(identityMatrix); + Gdip.Matrix_delete(savedMatrix); + } else { + Gdip.Font_GetLogFontW(font, graphics, pLogFont); + } outFont[0] = OS.CreateFontIndirect(pLogFont); OS.HeapFree(hHeap, 0, pLogFont); } diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java index 2806c1d865..ba27c86d0b 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java @@ -730,6 +730,45 @@ public void test_getFontMetrics() { assertTrue(fm.getHeight() > 0); } +/** + * Verifies that applying a transform to a GC does not affect the font metrics + * when the font does not exist on the current platform and a fallback font is + * used instead. + * + * @see Issue 2978 + */ +@Test +public void test_getFontMetrics_notAffectedByTransform() { + Font font = new Font(display, "NonExistentSWTTestFont", 24, SWT.NORMAL); + Transform scaleTransform = new Transform(display, 3.0f, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f); + try { + gc.setFont(font); + gc.setTransform(scaleTransform); + double metricsWithTransform = gc.getFontMetrics().getAverageCharacterWidth(); + + gc.setTransform(null); + double metricsAfterClearingTransform = gc.getFontMetrics().getAverageCharacterWidth(); + + gc.setFont(font); + double metricsAfterReSettingFont = gc.getFontMetrics().getAverageCharacterWidth(); + + gc.setTransform(scaleTransform); + double metricsWithTransformAgain = gc.getFontMetrics().getAverageCharacterWidth(); + + assertAll( + () -> assertEquals(metricsWithTransform, metricsAfterClearingTransform, 0, + "Font metrics must not change after clearing the transform"), + () -> assertEquals(metricsAfterClearingTransform, metricsAfterReSettingFont, 0, + "Font metrics must not change when re-setting the font without transform"), + () -> assertEquals(metricsAfterReSettingFont, metricsWithTransformAgain, 0, + "Font metrics must not be scaled when the transform is re-applied") + ); + } finally { + scaleTransform.dispose(); + font.dispose(); + } +} + @Test public void test_getStyle() { Canvas canvas = new Canvas(shell, SWT.NULL);