From 5e3cee18b367c8d6833070f0e27ea1ee2fee8283 Mon Sep 17 00:00:00 2001 From: Heiko Klare Date: Sat, 6 Jun 2026 13:28:55 +0200 Subject: [PATCH] [Win32] Normalise \r and \r\n to \n in GDI+ text rendering GDI+ Graphics_DrawString only treats \n as a line delimiter; bare \r is silently ignored rather than treated as a line break. The GDI DrawText path (non-advanced mode) and the old DrawDriverString path both accepted \r and \r\n as line delimiters, so the mismatch was a regression for callers that pass Windows-style (CRLF) or old Mac-style (CR-only) line endings with DRAW_DELIMITER set. Fix: in drawTextGDIP(), when DRAW_DELIMITER is active, normalise \r\n to \n first (to avoid double line breaks), then convert any remaining lone \r to \n before handing the string to Graphics_DrawString. Add a cross-platform regression test that verifies all three line-ending variants (\n, \r, \r\n) produce the same text height in both advanced and non-advanced rendering mode. Contributes to https://github.com/eclipse-platform/eclipse.platform.swt/issues/3091 --- .../win32/org/eclipse/swt/graphics/GC.java | 4 ++ .../Test_org_eclipse_swt_graphics_GC.java | 39 +++++++++++++++++++ 2 files changed, 43 insertions(+) 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 253411851f..890b681fb2 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 @@ -3040,6 +3040,10 @@ private void drawTextGDIP(long gdipGraphics, String string, int x, int y, int fl char[] buffer; if ((flags & SWT.DRAW_DELIMITER) == 0) { string = string.replaceAll("[\\r\\n]+", ""); + } else { + // Graphics_DrawString only recognises \n as a line break, not bare \r. + // Normalise \r\n -> \n first, then lone \r -> \n (order matters). + string = string.replace("\r\n", "\n").replace("\r", "\n"); } int length = string.length(); if (length != 0) { 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 8d5eafc830..78b385d651 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 @@ -968,6 +968,45 @@ public void test_textExtentLjava_lang_StringI_disabledLineDelimiter() { gc.dispose(); } +/** + * @see Issue 3091 + */ +@Test +public void test_textExtentLjava_lang_StringI_lineDelimiterVariants() { + // 中 has no glyph in common Latin fonts; on Win32 this causes useGDIP() to + // return true, routing text through drawTextGDIP() / Graphics_DrawString. + String withLF = "a中\nb"; + String withCR = "a中\rb"; + String withCRLF = "a中\r\nb"; + int flags = SWT.DRAW_DELIMITER; + + gc.setAdvanced(false); + Point lfNonAdv = gc.textExtent(withLF, flags); + Point crNonAdv = gc.textExtent(withCR, flags); + Point crlfNonAdv = gc.textExtent(withCRLF, flags); + + gc.setAdvanced(true); + Point lfAdv = gc.textExtent(withLF, flags); + Point crAdv = gc.textExtent(withCR, flags); + Point crlfAdv = gc.textExtent(withCRLF, flags); + + // All three line-ending types must produce the same text height in non-advanced mode. + assertEquals(lfNonAdv.y, crNonAdv.y, + "Non-advanced: CR must produce the same height as LF with DRAW_DELIMITER"); + assertEquals(lfNonAdv.y, crlfNonAdv.y, + "Non-advanced: CRLF must produce the same height as LF with DRAW_DELIMITER"); + + // Advanced mode must agree with non-advanced within minor rendering tolerance. + // On Win32, \r and \r\n must be normalised to \n before passing to + // Graphics_DrawString, which does not treat bare \r as a line delimiter. + assertTrue(Math.abs(lfAdv.y - lfNonAdv.y) <= 2, + "LF: advanced height must match non-advanced"); + assertTrue(Math.abs(crAdv.y - crNonAdv.y) <= 2, + "CR: advanced height must match non-advanced"); + assertTrue(Math.abs(crlfAdv.y - crlfNonAdv.y) <= 2, + "CRLF: advanced height must match non-advanced"); +} + @Test public void test_toString() { String s = gc.toString();