From 817f0a4a76d4d0bb1b644ff44eaaf9058b7a5cc6 Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Tue, 1 Sep 2026 16:25:52 +0200 Subject: [PATCH] Make SVG currentColor configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SVG icons using fill="currentColor" are rasterized with a hardcoded color in JSVGRasterizer. Until 2026-06 that was white (the AWT default), since 2026-09 it is black. Either default is wrong for one of the themes, so the color must be configurable to support both dark and light themes. Add a global current color, settable via JSVGRasterizer.setCurrentColor(RGB) or the system property swt.svg.currentColor. Default stays black and explicit fills are unaffected. Assisted-by: multiple AI agents and layers of automated tooling 🤖 --- .../org/eclipse/swt/svg/JSVGRasterizer.java | 49 ++++++++++++++++++- .../eclipse/swt/svg/JSVGRasterizerTest.java | 36 ++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java b/bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java index 80789414819..b95d554a83c 100644 --- a/bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java +++ b/bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java @@ -42,6 +42,7 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.PaletteData; +import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.internal.image.SVGRasterizer; import com.github.weisj.jsvg.SVGDocument; @@ -53,6 +54,11 @@ * A rasterizer implementation for converting SVG data into rasterized images. * This class uses the third party library JSVG for the raterization of SVG * images. + *

+ * The color resolving {@code currentColor} is global, defaults to black and + * can be set via {@link #setCurrentColor(RGB)} or the system property + * {@code swt.svg.currentColor} (e.g. {@code #FFFFFF}). + *

*/ public class JSVGRasterizer implements SVGRasterizer { @@ -69,6 +75,14 @@ public class JSVGRasterizer implements SVGRasterizer { } } + private static final String CURRENT_COLOR_PROPERTY = "swt.svg.currentColor"; //$NON-NLS-1$ + + private static final RGB DEFAULT_CURRENT_COLOR = new RGB(0, 0, 0); + + private static final RGB INITIAL_CURRENT_COLOR = readCurrentColorProperty(); + + private static volatile RGB currentColor = INITIAL_CURRENT_COLOR; + private final static Map RENDERING_HINTS = Map.of( // KEY_ANTIALIASING, VALUE_ANTIALIAS_ON, // KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY, // @@ -81,6 +95,38 @@ public class JSVGRasterizer implements SVGRasterizer { KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON // ); + /** + * Sets the color resolving {@code currentColor}. Applies to subsequent + * rasterizations only. + * + * @param color the color to use, or {@code null} to restore the initial value + */ + public static void setCurrentColor(RGB color) { + currentColor = color != null ? color : INITIAL_CURRENT_COLOR; + } + + /** + * Returns the color resolving {@code currentColor}. + * + * @return the current color, never {@code null} + */ + public static RGB getCurrentColor() { + return currentColor; + } + + private static RGB readCurrentColorProperty() { + String value = System.getProperty(CURRENT_COLOR_PROPERTY); + if (value == null) { + return DEFAULT_CURRENT_COLOR; + } + try { + int rgb = Integer.parseInt(value.trim().replaceFirst("^#", ""), 16); //$NON-NLS-1$ //$NON-NLS-2$ + return new RGB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF); + } catch (NumberFormatException e) { + return DEFAULT_CURRENT_COLOR; + } + } + @Override public ImageData rasterizeSVG(InputStream inputStream, int zoom) { if (zoom < 0) { @@ -140,7 +186,8 @@ private int calculateTargetHeight(float scalingFactor, FloatSize sourceImageSize private Graphics2D configureRenderingOptions(float widthScalingFactor, float heightScalingFactor, BufferedImage image) { Graphics2D g = image.createGraphics(); - g.setColor(Color.BLACK); + RGB color = currentColor; + g.setColor(new Color(color.red, color.green, color.blue)); g.setRenderingHints(RENDERING_HINTS); g.scale(widthScalingFactor, heightScalingFactor); return g; diff --git a/bundles/org.eclipse.swt.svg/test/org/eclipse/swt/svg/JSVGRasterizerTest.java b/bundles/org.eclipse.swt.svg/test/org/eclipse/swt/svg/JSVGRasterizerTest.java index 498d06eaa69..f46afa71652 100644 --- a/bundles/org.eclipse.swt.svg/test/org/eclipse/swt/svg/JSVGRasterizerTest.java +++ b/bundles/org.eclipse.swt.svg/test/org/eclipse/swt/svg/JSVGRasterizerTest.java @@ -96,4 +96,40 @@ void testDefaultCurrentColorIsBlack() { RGB rgb = data.palette.getRGB(data.getPixel(0, 0)); assertEquals(new RGB(0, 0, 0), rgb); } + + @Test + void testConfiguredCurrentColorIsUsed() { + try { + JSVGRasterizer.setCurrentColor(new RGB(255, 255, 255)); + ImageData data = rasterizer.rasterizeSVG(svgStream(svgString), 100); + RGB rgb = data.palette.getRGB(data.getPixel(0, 0)); + assertEquals(new RGB(255, 255, 255), rgb); + } finally { + JSVGRasterizer.setCurrentColor(null); + } + } + + @Test + void testResetCurrentColorRestoresInitialValue() { + JSVGRasterizer.setCurrentColor(new RGB(255, 0, 0)); + JSVGRasterizer.setCurrentColor(null); + assertEquals(new RGB(0, 0, 0), JSVGRasterizer.getCurrentColor()); + } + + @Test + void testCurrentColorDoesNotAffectExplicitFill() { + String explicitFill = """ + + + + """; + try { + JSVGRasterizer.setCurrentColor(new RGB(255, 255, 255)); + ImageData data = rasterizer.rasterizeSVG(svgStream(explicitFill), 100); + RGB rgb = data.palette.getRGB(data.getPixel(0, 0)); + assertEquals(new RGB(0, 255, 0), rgb); + } finally { + JSVGRasterizer.setCurrentColor(null); + } + } }