Repository: eclipse-platform/eclipse.platform.swt
Pull request #1638: Rasterization of SVGs at Runtime for Eclipse Icons 
Author: login: Michael5601
Created: 2024-12-06T13:23:11Z | Merged: 2025-03-25T07:59:50Z

Eclipse currently loads icons exclusively as raster graphics (e.g., `.png`), without support for vector formats like `.svg`. A major drawback of raster graphics is their inability to scale without degrading image quality. Additionally, generating icons of different sizes requires manually rasterizing SVGs outside Eclipse, leading to unnecessary effort and many icon files.

This PR introduces support for vector graphics in Eclipse, enabling SVGs to be used for icons. Existing PNG icons will continue to be loaded alongside SVGs, allowing the use of the new functionality without the need to replace all PNG files at once.

---

### **Key Features**

- **Example Images**:  
  Screenshots showcasing the new functionality can be found below. These screenshots were taken with 125% monitor-zoom and scaling enabled with the flag `-Dswt.autoScale=quarter`.
  
| PNG      | SVG      |
|---------------|---------------|
| ![PNG](https://github.com/user-attachments/assets/6ecd1eb2-8716-465f-9206-e64ae9e17e7a)  | ![SVG](https://github.com/user-attachments/assets/44d69279-18a0-46de-9972-a529eff5da66)  |
| ![PNG2](https://github.com/user-attachments/assets/0ba6c210-1b0e-4f75-8fc5-f8b40d5c97b4)  | ![SVG2](https://github.com/user-attachments/assets/d8892fd4-c374-4152-9458-b7d50ed54157)  |
| ![PNG3](https://github.com/user-attachments/assets/d3c5668f-a4a3-4b9f-8ad4-73072c258abc)  | ![SVG3](https://github.com/user-attachments/assets/8f20bbf6-30d8-41cb-9775-ddbdad7398a4)  |
| ![PNG4](https://github.com/user-attachments/assets/dbbd7b6c-4e58-415e-8a13-ba3557f2dca6)  | ![SVG4](https://github.com/user-attachments/assets/95270a6b-b550-4652-afe5-43d35444b1df)  |

  

- **How It Works**:  
  - To use SVG icons, simply place the SVG file in the bundle and reference it in the `plugin.xml` and other necessary locations, as is done for PNGs. No additional configuration is required.  
  - At runtime, Eclipse uses the library JSVG to rasterize the SVG into a raster image of the desired size, eliminating the need for scaling. My analysis shows that JSVG is the most suitable Java library for this purpose.
  - You need to write the flag `-Dswt.autoScale=quarter` into your `eclipse.ini` file or into the run arguments of a new configuration.

- **Demonstration**:  
  This PR includes SVG versions of icons for the following bundles:  
  - `org.eclipse.search`  
  - `org.eclipse.ui`  
  - `org.eclipse.ui.ide`  
  - `org.eclipse.ui.ide.application`  
  - `org.eclipse.ui.editors`  
  - `org.eclipse.ui.navigator`  

Due to this the PR of Platform UI is very large. If preferred, I can limit the changes to `org.eclipse.search`, allowing SVG icons to be tested specifically in the search result window.
I did not delete any exisiting PNGs. 

---

### **Architecture**

Eclipse icons fall into three categories, all three adressed by this PR:

1. **Standard Icons**:  
   These is the most common type of icons. They are loaded, scaled if needed, and displayed in the UI.

2. **Composite Icons**:  
   Composite icons combine a base icon with up to four overlay icons. Each component is loaded separately and then combined for display.

3. **Graphically Customized Icons**:  
   These icons represent disabled or "grayed-out" states. They can either be provided as pre-designed raster images or generated dynamically at runtime by applying transformations to the "enabled" version of the icon. Eclipse does not have no pre-designed SVGs. As soon as the path of the pre-designed raster graphic is removed the icon will be created at runtime. 

**Standard-Icons** and **Composite Icons** are fully supported by this PR. **Graphically Customized Icons** are only supported if the pre-designed raster graphic is removed. The original image loaded before the customization, is generated by rasterizing a SVG. Afterwards the current automatic customization functionality creates the "grayed-out" style. This currenct functionality for customization produces bad results as seen [here](https://github.com/eclipse-platform/eclipse.platform.swt/discussions/1619#discussioncomment-11408347). This is why I want to change this behaviour in a future PR.
 
The solution of my future PR applies a runtime filter to SVGs before rasterization (pre-processing), which can emulate GTK or Cocoa (macOS)/Windows styles. Specifically, GTK uses a unique style for disabled icons and I can only produce results that fit to either GTK or Windows and Cocoa. I will release this feature as a follow up to this PR as soon as a solution is found for the different styles for different OS. As discussion for this topic can be found [here](https://github.com/eclipse-platform/eclipse.platform.swt/discussions/1619). Alternatively I can release the PR before and we live with the other visuals for one OS. For now I recommend to use the pre-designed PNGs as usual. The future PR for the customization of icons with SVGs is nearly complete but requires further refinement before release. 

The sequence diagrams below illustrate the architecture for loading **Standard** and **Composite Icons**. The new functionality introduced in this PR is highlighted in blue. The new functionality changes the ImageLoader process on the right side of the diagramms. You can see that the implementation can happen in the same place for these two icon groups:

**Sequence-Diagram: Standard-Icons (Currenct Workflow)**
![20241206_Standard-Icons_Current](https://github.com/user-attachments/assets/b90fcf52-62df-4e18-8107-c7a4bd6174a7)

**Sequence-Diagram: Standard-Icons (Improved Workflow)**
![20241206_Standard-Icons_Improved](https://github.com/user-attachments/assets/4f6c0027-a6df-412f-9b44-24cda8c3a55c)

**Sequence-Diagram: Composite-Icons (Currenct Workflow. See also the two little referenced workflows below)**
![20241206_Composite-Icons_Current-1](https://github.com/user-attachments/assets/ada6a411-74b2-4f43-986f-b6e333e24743)
![20241206_Composite-Icons_Current-2](https://github.com/user-attachments/assets/fdeb4a40-0f96-422e-a760-8807b7585bfc)

**Sequence-Diagram: Composite-Icons (Improved Workflow)**
![20241206_Composite-Icons_Improved](https://github.com/user-attachments/assets/e4d84074-3082-4121-9f60-6e1c019412c4)

For **Graphically Customized Icons**, only the current workflow will be shown in a sequence diagram here. The new workflow as described above will be part of the follow up PR. You can see that by calling `URLImageDescriptor.createImage()` the Standard-Icons workflow is called. 

**Sequence-Diagram: Disabled-Icons (Current Workflow)**
![20241206_Disabled-Icons_Current](https://github.com/user-attachments/assets/2816f5c2-2518-4475-9a3c-ca8b76020be5)

---

### **Dependency Management**

To enable SVG rasterization, the library JSVG is required as an external dependency. Since all rasterization logic needs to be part of Eclipse SWT, the dependency must also be included in SWT. However, SWT currently has no external dependencies, which created challenges for the integration of the Dependency:

#### **Unsuccessful Approaches**:
1. **Fragment Approach**:  
   Adding a fragment to SWT with the JSVG dependency failed, as SWT itself is composed of fragments, and fragments cannot depend on other fragments.

2. **Service Loader Approach**:  
   Adding a new bundle with the functionality and the dependency failed because SWT fragments lack a class loader to locate the new bundle. (I am not sure if this reason is correct but I tried for some hours and it did not work)

#### **Successful Approach**:  
I created a new bundle containing the rasterization logic and the JSVG dependency. This bundle registers itself via a hook mechanism during initialization. To ensure the bundle is loaded, I added an initialization line to `Workbench.createAndRunWorkbench(Display, WorkbenchAdvisor)`.

Once JSVG is included in the target platform (with help from @HannesWell), this functionality will become fully operational.

---

### **Outstanding Issues**

~~1. **Target Platform**:  
   The JSVG library must be included in the target platform.~~

2. **Existing PNGs**:  
   PNG files cannot yet be deleted, as some are referenced across bundles via hardcoded paths e.g. in platform code. Removing these files would result in errors.

~~3. **Early-Loaded Icons**:  
   A few icons (e.g., `eclipse16.png`) are loaded before the workbench is initialized. At this point, the rasterizer has not yet been registered. I only experienced three icons that were loaded this early but there might be more.~~

4. **Scaling Support for Composite Icons**:  
I modified the behavior of the `CompositeImageDescriptor.supportsZoomLevel(int zoom)` method in Platform UI to address a limitation where it only allowed zoom levels that were exact multiples of 100 (e.g., 100, 200, 300). This restriction, caused by an unresolved bug, prevented scaling to zoom levels such as 125. Although I haven't analyzed the underlying bug yet, this change was necessary to enable proper scaling functionality.

5. **Missing or broken SVGs**
There are some icons that don't have a SVG-File or the SVG-File needs to be improved. The feature can still be used as there will be raster graphics that are loaded instead but finally this issue needs to be fixed. I will provide PRs if I find these icons. 
---

### **Planned Tasks for this PR**

~~1. **Regression Testing**:  
   I will add regression tests that allow automatic testing of the feature.~~

~~2. **Performance Evaluation**:  
   I will perform performance tests to find out if this feature is faster or slower than the current implementation. From manual testing I can say that it feels rather the same.~~

3. **Testing for different OS**
The `ImageLoader` class has specific implementations for cocoa, win32 and gtk that I needed to change. All three implementations are very similar, so I don't think there will be an issue but the cocoa and gtk implementation was no yet tested by me. I only tested on a windows system. 

---

See also this [PR](https://github.com/eclipse-platform/eclipse.platform.ui/pull/2593) for the necessary changes in Platform UI. All changes for this feature were performed in SWT and Platform UI. 

Fixes https://github.com/eclipse-platform/eclipse.platform.swt/issues/1438.

Let me know if you'd like further clarification or additional adjustments!



Diff:
diff --git a/bundles/org.eclipse.swt.svg/.classpath b/bundles/org.eclipse.swt.svg/.classpath
new file mode 100644
index 00000000000..81fe078c20c
--- /dev/null
+++ b/bundles/org.eclipse.swt.svg/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.swt.svg/.project b/bundles/org.eclipse.swt.svg/.project
new file mode 100644
index 00000000000..587ce542c7c
--- /dev/null
+++ b/bundles/org.eclipse.swt.svg/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.swt.svg</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.swt.svg/.settings/org.eclipse.core.resources.prefs b/bundles/org.eclipse.swt.svg/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 00000000000..99f26c0203a
--- /dev/null
+++ b/bundles/org.eclipse.swt.svg/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/bundles/org.eclipse.swt.svg/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.swt.svg/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000000..4b76983c5f1
--- /dev/null
+++ b/bundles/org.eclipse.swt.svg/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,10 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
+org.eclipse.jdt.core.compiler.compliance=17
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=17
+org.eclipse.jdt.core.incompleteClasspath=warning
diff --git a/bundles/org.eclipse.swt.svg/META-INF/MANIFEST.MF b/bundles/org.eclipse.swt.svg/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..d65dc50cfdf
--- /dev/null
+++ b/bundles/org.eclipse.swt.svg/META-INF/MANIFEST.MF
@@ -0,0 +1,13 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: org.eclipse.swt.svg
+Bundle-Version: 3.130.0.qualifier
+Automatic-Module-Name: org.eclipse.swt.svg
+Bundle-Name: %fragmentName
+Bundle-Vendor: %providerName
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Fragment-Host: org.eclipse.swt
+Import-Package: com.github.weisj.jsvg;version="[1.7.0,2.0.0)",
+ com.github.weisj.jsvg.geometry.size;version="[1.7.0,2.0.0)",
+ com.github.weisj.jsvg.parser;version="[1.7.0,2.0.0)"
+Export-Package: org.eclipse.swt.svg
diff --git a/bundles/org.eclipse.swt.svg/META-INF/services/org.eclipse.swt.internal.image.SVGRasterizer b/bundles/org.eclipse.swt.svg/META-INF/services/org.eclipse.swt.internal.image.SVGRasterizer
new file mode 100644
index 00000000000..bd95b6eea89
--- /dev/null
+++ b/bundles/org.eclipse.swt.svg/META-INF/services/org.eclipse.swt.internal.image.SVGRasterizer
@@ -0,0 +1 @@
+org.eclipse.swt.svg.JSVGRasterizer
diff --git a/bundles/org.eclipse.swt.svg/build.properties b/bundles/org.eclipse.swt.svg/build.properties
new file mode 100644
index 00000000000..bfdbd565a5b
--- /dev/null
+++ b/bundles/org.eclipse.swt.svg/build.properties
@@ -0,0 +1,15 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               fragment.properties
+
+tycho.pomless.parent = ../../local-build/local-build-parent
+jars.extra.classpath = platform:/plugin/org.eclipse.swt.cocoa.macosx.aarch64,\
+                       platform:/plugin/org.eclipse.swt.cocoa.macosx.x86_64,\
+                       platform:/plugin/org.eclipse.swt.gtk.linux.aarch64,\
+                       platform:/plugin/org.eclipse.swt.gtk.linux.ppc64le,\
+                       platform:/plugin/org.eclipse.swt.gtk.linux.riscv64,\
+                       platform:/plugin/org.eclipse.swt.gtk.linux.x86_64,\
+                       platform:/plugin/org.eclipse.swt.win32.win32.aarch64,\
+                       platform:/plugin/org.eclipse.swt.win32.win32.x86_64
diff --git a/bundles/org.eclipse.swt.svg/fragment.properties b/bundles/org.eclipse.swt.svg/fragment.properties
new file mode 100644
index 00000000000..1ae8d5b50da
--- /dev/null
+++ b/bundles/org.eclipse.swt.svg/fragment.properties
@@ -0,0 +1,14 @@
+###############################################################################
+# Copyright (c) 2025 Vector Informatik GmbH and others.
+#
+# This program and the accompanying materials are made available under the terms of the Eclipse
+# Public License 2.0 which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+#     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+###############################################################################
+fragmentName = SWT SVG Rendering Support
+providerName = Eclipse.org
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
new file mode 100644
index 00000000000..0da0c7872dc
--- /dev/null
+++ b/bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.KEY_ALPHA_INTERPOLATION;
+import static java.awt.RenderingHints.KEY_ANTIALIASING;
+import static java.awt.RenderingHints.KEY_COLOR_RENDERING;
+import static java.awt.RenderingHints.KEY_DITHERING;
+import static java.awt.RenderingHints.KEY_FRACTIONALMETRICS;
+import static java.awt.RenderingHints.KEY_INTERPOLATION;
+import static java.awt.RenderingHints.KEY_RENDERING;
+import static java.awt.RenderingHints.KEY_STROKE_CONTROL;
+import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING;
+import static java.awt.RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY;
+import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;
+import static java.awt.RenderingHints.VALUE_COLOR_RENDER_QUALITY;
+import static java.awt.RenderingHints.VALUE_DITHER_DISABLE;
+import static java.awt.RenderingHints.VALUE_FRACTIONALMETRICS_ON;
+import static java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC;
+import static java.awt.RenderingHints.VALUE_RENDER_QUALITY;
+import static java.awt.RenderingHints.VALUE_STROKE_PURE;
+import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints.Key;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.internal.image.SVGRasterizer;
+
+import com.github.weisj.jsvg.SVGDocument;
+import com.github.weisj.jsvg.geometry.size.FloatSize;
+import com.github.weisj.jsvg.parser.LoaderContext;
+import com.github.weisj.jsvg.parser.SVGLoader;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class uses the third party library JSVG for the raterization of SVG
+ * images.
+ */
+public class JSVGRasterizer implements SVGRasterizer {
+
+	private static final SVGLoader SVG_LOADER = new SVGLoader();
+
+	private final static Map<Key, Object> RENDERING_HINTS = Map.of( //
+			KEY_ANTIALIASING, VALUE_ANTIALIAS_ON, //
+			KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY, //
+			KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY, //
+			KEY_DITHERING, VALUE_DITHER_DISABLE, //
+			KEY_FRACTIONALMETRICS, VALUE_FRACTIONALMETRICS_ON, //
+			KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC, //
+			KEY_RENDERING, VALUE_RENDER_QUALITY, //
+			KEY_STROKE_CONTROL, VALUE_STROKE_PURE, //
+			KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON //
+	);
+
+	@Override
+	public ImageData rasterizeSVG(InputStream inputStream, int zoom) throws IOException {
+		SVGDocument svgDocument = loadSVG(inputStream);
+		if (svgDocument == null) {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		}
+		BufferedImage rasterizedImage = renderSVG(svgDocument, zoom);
+		return convertToSWTImageData(rasterizedImage);
+	}
+
+	private SVGDocument loadSVG(InputStream inputStream) {
+		return SVG_LOADER.load(inputStream, null, LoaderContext.createDefault());
+	}
+
+	private BufferedImage renderSVG(SVGDocument svgDocument, int zoom) {
+		float scalingFactor = zoom / 100.0f;
+		BufferedImage image = createImageBase(svgDocument, scalingFactor);
+		Graphics2D g = configureRenderingOptions(scalingFactor, image);
+		svgDocument.render(null, g);
+		g.dispose();
+		return image;
+	}
+
+	private BufferedImage createImageBase(SVGDocument svgDocument, float scalingFactor) {
+		FloatSize sourceImageSize = svgDocument.size();
+		int targetImageWidth = calculateTargetWidth(scalingFactor, sourceImageSize);
+		int targetImageHeight = calculateTargetHeight(scalingFactor, sourceImageSize);
+		return new BufferedImage(targetImageWidth, targetImageHeight, BufferedImage.TYPE_INT_ARGB);
+	}
+
+	private int calculateTargetWidth(float scalingFactor, FloatSize sourceImageSize) {
+		double sourceImageWidth = sourceImageSize.getWidth();
+		return (int) Math.round(sourceImageWidth * scalingFactor);
+	}
+
+	private int calculateTargetHeight(float scalingFactor, FloatSize sourceImageSize) {
+		double sourceImageHeight = sourceImageSize.getHeight();
+		return (int) Math.round(sourceImageHeight * scalingFactor);
+	}
+
+	private Graphics2D configureRenderingOptions(float scalingFactor, BufferedImage image) {
+		Graphics2D g = image.createGraphics();
+		g.setRenderingHints(RENDERING_HINTS);
+		g.scale(scalingFactor, scalingFactor);
+		return g;
+	}
+
+	private ImageData convertToSWTImageData(BufferedImage rasterizedImage) {
+		int width = rasterizedImage.getWidth();
+		int height = rasterizedImage.getHeight();
+		int[] pixels = ((DataBufferInt) rasterizedImage.getRaster().getDataBuffer()).getData();
+		PaletteData paletteData = new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF);
+		ImageData imageData = new ImageData(width, height, 32, paletteData);
+		int index = 0;
+		for (int y = 0; y < imageData.height; y++) {
+			for (int x = 0; x < imageData.width; x++) {
+				int alpha = (pixels[index] >> 24) & 0xFF;
+				imageData.setAlpha(x, y, alpha);
+				imageData.setPixel(x, y, pixels[index++]);
+			}
+		}
+		return imageData;
+	}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/SWT.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/SWT.java
index 010e828d1d8..db336185913 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/SWT.java	
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/SWT.java	
@@ -4465,7 +4465,6 @@ public class SWT {
 
 	/**
 	 * Image format constant indicating a SVG format image (value is 8).
-	 * <br>Note that this is a <em>HINT</em> and is currently only supported on GTK.
 	 *
 	 * @since 3.113
 	 */
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java
index ea524de8fd0..fd9c89a8347 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java	
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java	
@@ -53,6 +53,9 @@ public abstract class FileFormat {
 		try {
 			FORMAT_FACTORIES.add(OS2BMPFileFormat::new);
 		} catch (NoClassDefFoundError e) { } // ignore format
+		try {
+			FORMAT_FACTORIES.add(SVGFileFormat::new);
+		} catch (NoClassDefFoundError e) { } // ignore format
 	}
 
 	public static final int DEFAULT_ZOOM = 100;
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGFileFormat.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGFileFormat.java
new file mode 100644
index 00000000000..001b9b6828d
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGFileFormat.java	
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal.image;
+
+import java.io.*;
+import java.nio.charset.*;
+import java.util.*;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.DPIUtil.*;
+
+/**
+ * A {@link FileFormat} implementation for handling SVG (Scalable Vector
+ * Graphics) files.
+ * <p>
+ * This class detects SVG files based on their header and uses a registered
+ * {@link SVGRasterizer} service to rasterize SVG content.
+ * </p>
+ */
+public class SVGFileFormat extends FileFormat {
+
+	/** The instance of the registered {@link SVGRasterizer}. */
+	private static final SVGRasterizer RASTERIZER = ServiceLoader.load(SVGRasterizer.class).findFirst().orElse(null);
+
+	@Override
+	boolean isFileFormat(LEDataInputStream stream) throws IOException {
+		byte[] firstBytes = new byte[5];
+		int bytesRead = stream.read(firstBytes);
+		stream.unread(firstBytes);
+		String header = new String(firstBytes, 0, bytesRead, StandardCharsets.UTF_8).trim();
+		return header.startsWith("<?xml") || header.startsWith("<svg");
+	}
+
+	@Override
+	List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom) {
+		if (RASTERIZER == null) {
+			SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT, null, " [No SVG rasterizer found]");
+		}
+		if (targetZoom <= 0) {
+			SWT.error(SWT.ERROR_INVALID_ARGUMENT, null, " [Cannot rasterize SVG for zoom <= 0]");
+		}
+		try {
+			ImageData rasterizedImageData = RASTERIZER.rasterizeSVG(inputStream, 100 * targetZoom / fileZoom);
+			return List.of(new ElementAtZoom<>(rasterizedImageData, targetZoom));
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_INVALID_IMAGE, e);
+			return List.of();
+		}
+	}
+
+	@Override
+	void unloadIntoByteStream(ImageLoader loader) {
+		throw new UnsupportedOperationException();
+	}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGRasterizer.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGRasterizer.java
new file mode 100644
index 00000000000..9586abfb5c6
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGRasterizer.java	
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal.image;
+
+import java.io.*;
+
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ */
+public interface SVGRasterizer {
+	/**
+	 * Rasterizes an SVG image from the provided {@code InputStream} using the
+	 * specified zoom.
+	 *
+	 * @param stream the SVG image as an {@link InputStream}.
+	 * @param zoom   the scaling factor (in percent) e.g. {@code 200} for doubled
+	 *               size. This value must be greater zero.
+	 * @return the {@link ImageData} for the rasterized image, or {@code null} if
+	 *         the input is not a valid SVG file or cannot be processed.
+	 */
+	public ImageData rasterizeSVG(InputStream stream, int zoom) throws IOException;
+}
diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/AllNonBrowserTests.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/AllNonBrowserTests.java
index af9a1bd017d..2f5ae54445e 100644
--- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/AllNonBrowserTests.java	
+++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/AllNonBrowserTests.java	
@@ -45,6 +45,7 @@
 		Test_org_eclipse_swt_accessibility_AccessibleControlEvent.class,
 		Test_org_eclipse_swt_accessibility_AccessibleEvent.class,
 		Test_org_eclipse_swt_accessibility_AccessibleTextEvent.class,
+		Test_org_eclipse_swt_internal_SVGRasterizer.class,
 		DPIUtilTests.class})
 public class AllNonBrowserTests {
 	private static List<Error> leakedResources;
diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_internal_SVGRasterizer.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_internal_SVGRasterizer.java
new file mode 100644
index 00000000000..32a02163727
--- /dev/null
+++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_internal_SVGRasterizer.java	
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.tests.junit;
+
+import static org.eclipse.swt.tests.junit.SwtTestUtil.assertSWTProblem;
+import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageDataProvider;
+import org.eclipse.swt.graphics.ImageFileNameProvider;
+import org.eclipse.swt.widgets.Display;
+import org.junit.Test;
+
+/**
+ * When executed locally (outside Tycho build), this tests needs to be run as
+ * JUnit plug-in test in order to have the SVGRasterizer fragment on the
+ * classpath.
+ */
+public class Test_org_eclipse_swt_internal_SVGRasterizer {
+
+	@Test
+	public void test_ConstructorLorg_eclipse_swt_graphics_Device_ImageFileNameProvider() {
+		ImageFileNameProvider validImageFileNameProvider = zoom -> getPath("collapseall.svg");
+		Image image = new Image(Display.getDefault(), validImageFileNameProvider);
+		image.dispose();
+
+		ImageFileNameProvider corruptImageFileNameProvider = zoom -> getPath("corrupt.svg");
+		SWTException e = assertThrows(SWTException.class,
+				() -> new Image(Display.getDefault(), corruptImageFileNameProvider));
+		assertSWTProblem("Incorrect exception thrown for provider with corrupt images", SWT.ERROR_INVALID_IMAGE, e);
+	}
+
+	@Test
+	public void test_ConstructorLorg_eclipse_swt_graphics_Device_ImageDataProvider() {
+		ImageDataProvider validImageDataProvider = zoom -> {
+			String fileName = "collapseall.svg";
+			return new ImageData(getPath(fileName));
+		};
+		Image image = new Image(Display.getDefault(), validImageDataProvider);
+		image.dispose();
+
+		ImageDataProvider corruptImageDataProvider = zoom -> {
+			String fileName = "corrupt.svg";
+			return new ImageData(getPath(fileName));
+		};
+		SWTException e = assertThrows(SWTException.class,
+				() -> new Image(Display.getDefault(), corruptImageDataProvider));
+		assertSWTProblem("Incorrect exception thrown for provider with corrupt images", SWT.ERROR_INVALID_IMAGE, e);
+	}
+
+	String getPath(String fileName) {
+		String urlPath = "";
+		String pluginPath = System.getProperty("PLUGIN_PATH");
+		if (pluginPath == null) {
+			URL url = getClass().getClassLoader().getResource(fileName);
+			if (url == null) {
+				fail("URL == null for file " + fileName);
+			}
+			urlPath = url.getFile();
+		} else {
+			urlPath = pluginPath + "/data/" + fileName;
+		}
+		if (File.separatorChar != '/')
+			urlPath = urlPath.replace('/', File.separatorChar);
+		if (SwtTestUtil.isWindows && urlPath.indexOf(File.separatorChar) == 0)
+			urlPath = urlPath.substring(1);
+		urlPath = urlPath.replaceAll("%20", " ");
+		// Fallback when test is locally executed as plug-in test
+		if (!Files.exists(Path.of(urlPath))) {
+			urlPath = Path.of("data/" + fileName).toAbsolutePath().toString();
+		}
+		assertTrue(Files.exists(Path.of(urlPath)), "file not found: " + urlPath);
+		return urlPath;
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.swt.tests/META-INF/p2.inf b/tests/org.eclipse.swt.tests/META-INF/p2.inf
new file mode 100644
index 00000000000..d63e5216db8
--- /dev/null
+++ b/tests/org.eclipse.swt.tests/META-INF/p2.inf
@@ -0,0 +1,4 @@
+# pull in the applicable implementation fragment at build time (bug 461427)
+requires.0.namespace = org.eclipse.equinox.p2.iu
+requires.0.name = org.eclipse.swt.svg
+requires.0.range = 0.0.0
diff --git a/tests/org.eclipse.swt.tests/data/collapseall.svg b/tests/org.eclipse.swt.tests/data/collapseall.svg
new file mode 100644
index 00000000000..587c3c3497e
--- /dev/null
+++ b/tests/org.eclipse.swt.tests/data/collapseall.svg
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="collapseall.svg">
+  <defs
+     id="defs4">
+    <linearGradient
+       y2="1047.3622"
+       x2="-15"
+       y1="1047.3622"
+       x1="-12"
+       gradientTransform="translate(18,-3)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5062-9"
+       xlink:href="#blue_gradient_left"
+       inkscape:collect="always" />
+           <linearGradient
+       y2="1047.3622"
+       x2="-15"
+       y1="1047.3622"
+       x1="-12"
+       gradientTransform="translate(20,-1)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5062-9-5"
+       xlink:href="#blue_gradient_left"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       id="blue_gradient_left">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="0"
+         id="blue_gradient_left_stop_0" />
+      <stop
+         style="stop-color:#c3e9ff;stop-opacity:1"
+         offset="1"
+         id="blue_gradient_left_stop_1" />
+    </linearGradient>
+       
+    <linearGradient
+       y2="1045.3622"
+       x2="-11"
+       y1="1042.3622"
+       x1="-11"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4975-2-1"
+       xlink:href="#blue_gradient_top"
+       inkscape:collect="always"
+       gradientTransform="translate(18,-3)" />
+       
+    <linearGradient
+       y2="1045.3622"
+       x2="-11"
+       y1="1042.3622"
+       x1="-11"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4975-2-1-1"
+       xlink:href="#blue_gradient_top"
+       inkscape:collect="always"
+       gradientTransform="translate(20,-1)" />
+    <linearGradient
+       id="blue_gradient_top"
+       inkscape:collect="always">
+      <stop
+         id="blue_gradient_top_stop_0"
+         offset="0"
+         style="stop-color:#c3e9ff;stop-opacity:1" />
+      <stop
+         id="blue_gradient_top_stop_1"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+       
+    <linearGradient
+       y2="1043.3622"
+       x2="-11"
+       y1="1042.3622"
+       x1="-11"
+       gradientTransform="translate(22,4)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4970"
+       xlink:href="#minus_shadow_gradient"
+       inkscape:collect="always" />
+    <linearGradient
+       id="minus_shadow_gradient">
+      <stop
+         style="stop-color:#c3e9ff;stop-opacity:0.49019608;"
+         offset="0"
+         id="minus_shadow_gradient_stop_0" />
+      <stop
+         id="minus_shadow_gradient_stop_1"
+         offset="0.5"
+         style="stop-color:#c3e9ff;stop-opacity:1;" />
+      <stop
+         style="stop-color:#c3e9ff;stop-opacity:0.49019608;"
+         offset="1"
+         id="minus_shadow_gradient_stop_2" />
+    </linearGradient>
+
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="22.627417"
+     inkscape:cx="8.1029364"
+     inkscape:cy="7.6300717"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1138"
+     inkscape:window-height="951"
+     inkscape:window-x="1043"
+     inkscape:window-y="364"
+     inkscape:window-maximized="0"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3999" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     style="display:inline"
+     transform="translate(0,-1036.3622)">
+    <path
+       style="color:#ffffff;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+       d="m 3,1039.2997 8.971875,0 0.02812,9.1563 -8.971875,0 z"
+       id="front_background-5"
+       inkscape:connector-curvature="0" />
+    <path
+       style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#a6afc4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+       d="m 2,1038.3622 0,11 11,0 0,-11 z m 1,1 9,0 0,9 -9,0 z"
+       id="back_rect"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <path
+       style="fill:url(#linearGradient5062-9);fill-opacity:1.0;stroke:none;display:inline"
+       d="m 5,1041.3622 0,7 -2,0 0,-9 z"
+       id="back_left_shadow"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:url(#linearGradient4975-2-1);fill-opacity:1.0;stroke:none;display:inline"
+       d="m 5,1041.3622 7,0 0,-2 -9,0 z"
+       id="back_top_shadow"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#8b96ab;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+       d="m 4,1040.3622 0,11 11,0 0,-11 z m 1,1 9,0 0,9 -9,0 z"
+       id="front_rect"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <path
+       style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+       d="m 5,1041.3622 8.971875,0 0.02812,9 -8.971875,0 z"
+       id="front_background"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:url(#linearGradient4970);fill-opacity:1.0;stroke:none;display:inline"
+       d="m 8,1047.3622 5,0 c 1,0 1,-1 0,-1 l -5,0 c -1,0 -1,1 0,1 z"
+       id="minus_sign_shadow"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:url(#linearGradient5062-9-5);fill-opacity:1;stroke:none;display:inline"
+       d="m 7,1043.3622 0,7 -2,0 0,-9 z"
+       id="left_top_shadow"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:url(#linearGradient4975-2-1-1);fill-opacity:1;stroke:none;display:inline"
+       d="m 7,1043.3622 7,0 0,-2 -9,0 z"
+       id="front_top_shadow"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <rect
+       style="fill:#01467a;fill-opacity:1;stroke:none;display:inline"
+       id="minus_sign"
+       width="1"
+       height="7"
+       x="1045.3622"
+       y="-13"
+       transform="matrix(0,1,-1,0,0,0)" />
+  </g>
+</svg>
diff --git a/tests/org.eclipse.swt.tests/data/corrupt.svg b/tests/org.eclipse.swt.tests/data/corrupt.svg
new file mode 100644
index 00000000000..edac6e7e9f2
--- /dev/null
+++ b/tests/org.eclipse.swt.tests/data/corrupt.svg
@@ -0,0 +1,210 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<   inkscape:version="0.91 r13725"
+   sodipodi:docname="collapseall.svg">
+  <defs
+     id="defs4">
+    <linearGradient
+       y2="1047.3622"
+       x2="-15"
+       y1="1047.3622"
+       x1="-12"
+       gradientTransform="translate(18,-3)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5062-9"
+       xlink:href="#blue_gradient_left"
+       inkscape:collect="always" />
+           <linearGradient
+       y2="1047.3622"
+       x2="-15"
+       y1="1047.3622"
+       x1="-12"
+       gradientTransform="translate(20,-1)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5062-9-5"
+       xlink:href="#blue_gradient_left"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       id="blue_gradient_left">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="0"
+         id="blue_gradient_left_stop_0" />
+      <stop
+         style="stop-color:#c3e9ff;stop-opacity:1"
+         offset="1"
+         id="blue_gradient_left_stop_1" />
+    </linearGradient>
+       
+    <linearGradient
+       y2="1045.3622"
+       x2="-11"
+       y1="1042.3622"
+       x1="-11"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4975-2-1"
+       xlink:href="#blue_gradient_top"
+       inkscape:collect="always"
+       gradientTransform="translate(18,-3)" />
+       
+    <linearGradient
+       y2="1045.3622"
+       x2="-11"
+       y1="1042.3622"
+       x1="-11"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4975-2-1-1"
+       xlink:href="#blue_gradient_top"
+       inkscape:collect="always"
+       gradientTransform="translate(20,-1)" />
+    <linearGradient
+       id="blue_gradient_top"
+       inkscape:collect="always">
+      <stop
+         id="blue_gradient_top_stop_0"
+         offset="0"
+         style="stop-color:#c3e9ff;stop-opacity:1" />
+      <stop
+         id="blue_gradient_top_stop_1"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+       
+    <linearGradient
+       y2="1043.3622"
+       x2="-11"
+       y1="1042.3622"
+       x1="-11"
+       gradientTransform="translate(22,4)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4970"
+       xlink:href="#minus_shadow_gradient"
+       inkscape:collect="always" />
+    <linearGradient
+       id="minus_shadow_gradient">
+      <stop
+         style="stop-color:#c3e9ff;stop-opacity:0.49019608;"
+         offset="0"
+         id="minus_shadow_gradient_stop_0" />
+      <stop
+         id="minus_shadow_gradient_stop_1"
+         offset="0.5"
+         style="stop-color:#c3e9ff;stop-opacity:1;" />
+      <stop
+         style="stop-color:#c3e9ff;stop-opacity:0.49019608;"
+         offset="1"
+         id="minus_shadow_gradient_stop_2" />
+    </linearGradient>
+
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="22.627417"
+     inkscape:cx="8.1029364"
+     inkscape:cy="7.6300717"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1138"
+     inkscape:window-height="951"
+     inkscape:window-x="1043"
+     inkscape:window-y="364"
+     inkscape:window-maximized="0"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:snap-global="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3999" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     style="display:inline"
+     transform="translate(0,-1036.3622)">
+    <path
+       style="color:#ffffff;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+       d="m 3,1039.2997 8.971875,0 0.02812,9.1563 -8.971875,0 z"
+       id="front_background-5"
+       inkscape:connector-curvature="0" />
+    <path
+       style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#a6afc4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+       d="m 2,1038.3622 0,11 11,0 0,-11 z m 1,1 9,0 0,9 -9,0 z"
+       id="back_rect"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <path
+       style="fill:url(#linearGradient5062-9);fill-opacity:1.0;stroke:none;display:inline"
+       d="m 5,1041.3622 0,7 -2,0 0,-9 z"
+       id="back_left_shadow"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:url(#linearGradient4975-2-1);fill-opacity:1.0;stroke:none;display:inline"
+       d="m 5,1041.3622 7,0 0,-2 -9,0 z"
+       id="back_top_shadow"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#8b96ab;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+       d="m 4,1040.3622 0,11 11,0 0,-11 z m 1,1 9,0 0,9 -9,0 z"
+       id="front_rect"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <path
+       style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+       d="m 5,1041.3622 8.971875,0 0.02812,9 -8.971875,0 z"
+       id="front_background"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:url(#linearGradient4970);fill-opacity:1.0;stroke:none;display:inline"
+       d="m 8,1047.3622 5,0 c 1,0 1,-1 0,-1 l -5,0 c -1,0 -1,1 0,1 z"
+       id="minus_sign_shadow"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:url(#linearGradient5062-9-5);fill-opacity:1;stroke:none;display:inline"
+       d="m 7,1043.3622 0,7 -2,0 0,-9 z"
+       id="left_top_shadow"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:url(#linearGradient4975-2-1-1);fill-opacity:1;stroke:none;display:inline"
+       d="m 7,1043.3622 7,0 0,-2 -9,0 z"
+       id="front_top_shadow"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <rect
+       style="fill:#01467a;fill-opacity:1;stroke:none;display:inline"
+       id="minus_sign"
+       width="1"
+       height="7"
+       x="1045.3622"
+       y="-13"
+       transform="matrix(0,1,-1,0,0,0)" />
+  </g>
+</svg>


Reviews:
user: login: weisJ
state: COMMENTED
author_association: NONE
submitted_at: 2024-12-07T08:49:01Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-10T09:03:25Z
user: login: DevCharly
state: COMMENTED
author_association: NONE
submitted_at: 2024-12-10T14:32:18Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-10T15:25:38Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-10T15:48:11Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-10T16:12:32Z
user: login: laeubi
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T06:02:11Z
user: login: akurtakov
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T07:11:07Z
user: login: akurtakov
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T07:11:44Z
user: login: akurtakov
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T07:15:17Z
user: login: akurtakov
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T07:15:35Z
user: login: akurtakov
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T07:16:04Z
user: login: akurtakov
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T07:17:08Z
user: login: akurtakov
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T07:19:41Z
user: login: laeubi
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T07:28:17Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T07:30:49Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T07:30:56Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T07:31:08Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T07:32:02Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T07:35:36Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T07:40:44Z
user: login: laeubi
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T07:42:28Z
user: login: akurtakov
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T07:43:12Z
user: login: akurtakov
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T07:44:50Z
user: login: laeubi
body: I think we should follow the java recommendation for interface names here the `I` prefix is more an eclipse thing and even there we do not follow this anymore and I don't have seen it in SWT aynwhere
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T07:45:39Z
user: login: akurtakov
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T07:48:50Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T07:50:52Z
user: login: laeubi
body: I think the stream handling is flawed at the moment and reading SVG documents should be done using streams or URLs.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T07:54:09Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T07:55:34Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T08:06:58Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T08:07:10Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T08:09:56Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T08:13:06Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T08:20:52Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T08:23:41Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T08:52:55Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T08:56:20Z
user: login: laeubi
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T09:01:11Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T09:11:10Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T10:12:46Z
user: login: weisJ
state: COMMENTED
author_association: NONE
submitted_at: 2024-12-11T12:54:54Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T14:11:12Z
user: login: laeubi
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-11T14:23:12Z
user: login: HannesWell
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T22:40:51Z
user: login: HannesWell
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T22:51:54Z
user: login: HannesWell
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T23:03:43Z
user: login: HannesWell
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T23:13:30Z
user: login: HannesWell
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-11T23:21:57Z
user: login: laeubi
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-12T07:01:19Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-12T09:33:10Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-12T09:33:21Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-12T09:50:16Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-12T09:56:48Z
user: login: laeubi
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-12T10:14:38Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-12T10:25:24Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-12T10:38:45Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-12-12T13:47:57Z
user: login: HannesWell
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-12-17T21:37:04Z
user: login: HannesWell
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-01-19T12:42:42Z
user: login: HannesWell
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-01-19T12:53:18Z
user: login: HannesWell
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-01-19T14:12:40Z
user: login: HannesWell
body: Thanks for the update. I this is heading into the right direction.

I have added a few more comments below.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-01-22T00:21:04Z
user: login: HannesWell
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-01-26T22:04:01Z
user: login: HannesWell
body: @Michael5601 I have pushed two more commits that both should be incorporated into yours eventually.
The first one uses jsvg from the Target-Platform instead of embedding it, now that it's available and applies some more further clean-ups.
And the second one finally makes the build work, but requires a change in Tycho:
- https://github.com/eclipse-tycho/tycho/pull/4743

Regarding the tests:
First the new test-bundle should be moved into the `tests` folder and second, I'm not sure if we should really introduce a new test bundle just for that case. This seems to be a great overhead. On the other hand this allows to test SWT with the SVG support being absent.

state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-23T21:34:53Z
user: login: laeubi
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-24T05:32:05Z
user: login: HannesWell
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-24T08:48:51Z
user: login: laeubi
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-24T08:56:40Z
user: login: HannesWell
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-01T17:13:36Z
user: login: HeikoKlare
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T15:12:10Z
user: login: HannesWell
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T22:40:49Z
user: login: HannesWell
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T22:41:33Z
user: login: HeikoKlare
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T16:52:36Z
user: login: HeikoKlare
body: The PR looks almost good now. I have some remaining comments. In particular, there is a wrong zoom value used in the DynamicImageFileFormat.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T19:52:06Z
user: login: HeikoKlare
body: Some small additional comments I received from Hannes and sound reasonable to me.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T08:38:54Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T11:37:10Z
user: login: HeikoKlare
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T11:42:40Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T11:49:00Z
user: login: HeikoKlare
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T16:52:11Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T08:35:45Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T08:35:52Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T08:36:01Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T08:39:00Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T08:39:10Z
user: login: HannesWell
body: Thank you for all the updates Michael and Heiko.
After a quick review I think this is now in a really good shape (just one minor remark below) and I'm happy about the progress that was made here. I try to add my (hopefully final) detailed review tomorrow.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-23T20:08:42Z
user: login: Michael5601
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T08:18:36Z
user: login: HannesWell
body: After a detailed review I applied a few small adjustments to stream-line the code and the tests a little bit.
In my opinion this is finally ready for submission.

Thanks a lot for all your work on this topic, I'm really looking forward to have this available!🎉
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-24T22:22:38Z
user: login: HeikoKlare
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T07:59:36Z

Review comments:
diff_hunk: @@ -0,0 +1,69 @@
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.*;
+
+import java.awt.*;
+import java.awt.image.*;
+import java.io.*;
+import java.util.*;
+import org.eclipse.swt.graphics.ISVGRasterizer;
+import org.eclipse.swt.graphics.SVGRasterizerRegistry;
+import org.eclipse.swt.graphics.SVGUtil;
+
+import com.github.weisj.jsvg.*;
+import com.github.weisj.jsvg.geometry.size.*;
+import com.github.weisj.jsvg.parser.*;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class implements the {@code ISVGRasterizer} interface.
+ * 
+ * @since 1.0.0
+ */
+public class SVGRasterizer implements ISVGRasterizer {
+
+	/**
+	 * Initializes the SVG rasterizer by registering an instance of this rasterizer
+	 * with the {@link SVGRasterizerRegistry}.
+	 */
+	public static void intializeSVGRasterizer() {
+		SVGRasterizerRegistry.register(new SVGRasterizer());
+	}
+
+	private final static Map<Key, Object> RENDERING_HINTS = Map.of(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON, //
+			KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY, //
+			KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY, //
+			KEY_DITHERING, VALUE_DITHER_DISABLE, //
+			KEY_FRACTIONALMETRICS, VALUE_FRACTIONALMETRICS_ON, //
+			KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC, //
+			KEY_RENDERING, VALUE_RENDER_QUALITY, //
+			KEY_STROKE_CONTROL, VALUE_STROKE_PURE, //
+			KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON //
+	);
+
+	@Override
+	public BufferedImage rasterizeSVG(byte[] bytes, float scalingFactor) throws IOException {
+		SVGLoader loader = new SVGLoader();
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/SVGRasterizer.java
user: login: weisJ
body: If SVGs are only every rasterised from the same thread then this value could be a class member. Initialising it is relatively costly (as it initialises an XML reader internally).
created_at: 2024-12-07T08:49:01Z
author_association: NONE
diff_hunk: @@ -0,0 +1,69 @@
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.*;
+
+import java.awt.*;
+import java.awt.image.*;
+import java.io.*;
+import java.util.*;
+import org.eclipse.swt.graphics.ISVGRasterizer;
+import org.eclipse.swt.graphics.SVGRasterizerRegistry;
+import org.eclipse.swt.graphics.SVGUtil;
+
+import com.github.weisj.jsvg.*;
+import com.github.weisj.jsvg.geometry.size.*;
+import com.github.weisj.jsvg.parser.*;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class implements the {@code ISVGRasterizer} interface.
+ * 
+ * @since 1.0.0
+ */
+public class SVGRasterizer implements ISVGRasterizer {
+
+	/**
+	 * Initializes the SVG rasterizer by registering an instance of this rasterizer
+	 * with the {@link SVGRasterizerRegistry}.
+	 */
+	public static void intializeSVGRasterizer() {
+		SVGRasterizerRegistry.register(new SVGRasterizer());
+	}
+
+	private final static Map<Key, Object> RENDERING_HINTS = Map.of(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON, //
+			KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY, //
+			KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY, //
+			KEY_DITHERING, VALUE_DITHER_DISABLE, //
+			KEY_FRACTIONALMETRICS, VALUE_FRACTIONALMETRICS_ON, //
+			KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC, //
+			KEY_RENDERING, VALUE_RENDER_QUALITY, //
+			KEY_STROKE_CONTROL, VALUE_STROKE_PURE, //
+			KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON //
+	);
+
+	@Override
+	public BufferedImage rasterizeSVG(byte[] bytes, float scalingFactor) throws IOException {
+		SVGLoader loader = new SVGLoader();
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/SVGRasterizer.java
user: login: Michael5601
body: Thank you for this hint. It feels like this is a big performance boost.

created_at: 2024-12-10T09:03:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,27 @@
+package org.eclipse.swt.graphics;
+
+import java.awt.image.*;
+import java.io.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface ISVGRasterizer {
+
+	/**
+	 * Rasterizes an SVG image from the provided byte array, using the specified
+	 * zoom factor.
+	 *
+	 * @param bytes         the SVG image as a byte array.
+	 * @param scalingFactor the scaling ratio e.g. 2.0 for doubled size.
+	 * @return a {@link BufferedImage} containing the rasterized image, or
+	 *         {@code null} if the input is not a valid SVG file or cannot be
+	 *         processed.
+	 * @throws IOException if an error occurs while reading the SVG data.
+	 */
+	public BufferedImage rasterizeSVG(byte[] bytes, float scalingFactor) throws IOException;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
user: login: DevCharly
body: Why not use `org.eclipse.swt.graphics.ImageData` here?

Using AWT class `java.awt.image.BufferedImage` in public SWT API is probably a bad idea, isn't it?
This means that all implementations must either use AWT for rendering, or if using some native library then an additional conversion from native image data to AWT BufferedImage would be necessary.
created_at: 2024-12-10T14:25:43Z
author_association: NONE
diff_hunk: @@ -149,10 +152,72 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    byte[] bytes = null;
+	try {
+		bytes = stream.readAllBytes();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	ISVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+	    try {
+	    	float scalingFactor = zoom / 100.0f;
+	    	BufferedImage image = rasterizer.rasterizeSVG(bytes, scalingFactor);
+	    	if(image != null) {
+	    		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+	        	    ImageIO.write(image, "png", baos);
+	        	    try (InputStream in = new ByteArrayInputStream(baos.toByteArray())) {
+	        	    	data = FileFormat.load(in, this);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: DevCharly
body: This is a quick implementation, but it has some drawbacks. Uses extra memory for the PNG data and for the two conversions (BufferedImage -> PNG -> ImageData). Uses extra CPU time for PNG compression and decompression.

Would be probably better to directly convert `BufferedImage` into `ImageData`.
Don't know whether such utility method exists in Eclipse, but google shows some results:
https://www.google.com/search?client=firefox-b-d&q=awt+bufferedimage+to+swt+imagedata
created_at: 2024-12-10T14:31:45Z
author_association: NONE
diff_hunk: @@ -0,0 +1,27 @@
+package org.eclipse.swt.graphics;
+
+import java.awt.image.*;
+import java.io.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface ISVGRasterizer {
+
+	/**
+	 * Rasterizes an SVG image from the provided byte array, using the specified
+	 * zoom factor.
+	 *
+	 * @param bytes         the SVG image as a byte array.
+	 * @param scalingFactor the scaling ratio e.g. 2.0 for doubled size.
+	 * @return a {@link BufferedImage} containing the rasterized image, or
+	 *         {@code null} if the input is not a valid SVG file or cannot be
+	 *         processed.
+	 * @throws IOException if an error occurs while reading the SVG data.
+	 */
+	public BufferedImage rasterizeSVG(byte[] bytes, float scalingFactor) throws IOException;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
user: login: Michael5601
body: I tried to move the conversion you mentioned in your second comment into the class `SVGRasterizer` but here I see some problems:

1. There are different implementations for loading `ImageData` for different OS. Which should be chosen inside the `SVGRasterizer`?
2. If I choose the implementation from win32 or cocoa I have the issue that `FileFormat` is an internal class which I don't have access to. 
created_at: 2024-12-10T15:25:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,10 +152,72 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    byte[] bytes = null;
+	try {
+		bytes = stream.readAllBytes();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	ISVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+	    try {
+	    	float scalingFactor = zoom / 100.0f;
+	    	BufferedImage image = rasterizer.rasterizeSVG(bytes, scalingFactor);
+	    	if(image != null) {
+	    		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+	        	    ImageIO.write(image, "png", baos);
+	        	    try (InputStream in = new ByteArrayInputStream(baos.toByteArray())) {
+	        	    	data = FileFormat.load(in, this);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: Michael5601
body: I tried to find a better way for the conversion from `BufferedImage` to `InputStream` but this seems to be the best way. I did not yet try the conversion you are proposing (`BufferedImage` -> `ImageData`). It seems to be much code in the link you provided and I don't know if we need different implementation for different OS as seen in the existing conversions. (PNG -> `ImageData`)
Are you sure this way is significantly faster? We would go from 3 lines to 60+ lines of code for this and there is the possibility that it does not work properly for every case.

I will try if the solution from the link is working. 
created_at: 2024-12-10T15:48:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,10 +152,72 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    byte[] bytes = null;
+	try {
+		bytes = stream.readAllBytes();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	ISVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+	    try {
+	    	float scalingFactor = zoom / 100.0f;
+	    	BufferedImage image = rasterizer.rasterizeSVG(bytes, scalingFactor);
+	    	if(image != null) {
+	    		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+	        	    ImageIO.write(image, "png", baos);
+	        	    try (InputStream in = new ByteArrayInputStream(baos.toByteArray())) {
+	        	    	data = FileFormat.load(in, this);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: Michael5601
body: Update: It seems to work pretty good and also feels faster. I will test this with the future performance tests. 
created_at: 2024-12-10T16:12:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,10 +152,72 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    byte[] bytes = null;
+	try {
+		bytes = stream.readAllBytes();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	ISVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+	    try {
+	    	float scalingFactor = zoom / 100.0f;
+	    	BufferedImage image = rasterizer.rasterizeSVG(bytes, scalingFactor);
+	    	if(image != null) {
+	    		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+	        	    ImageIO.write(image, "png", baos);
+	        	    try (InputStream in = new ByteArrayInputStream(baos.toByteArray())) {
+	        	    	data = FileFormat.load(in, this);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: laeubi
body: > Would be probably better to directly convert BufferedImage into ImageData.
Don't know whether such utility method exists in Eclipse, but google shows some results

I have somewhere posted such thing at eclipse but couldn't find it. And yes it requires a lot of code. The main concern a while back to add such thing was that it creates a dependency to AWT (due to `BufferedImage`) @akurtakov  can maybe tell more.
created_at: 2024-12-11T06:02:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,73 @@
+package org.eclipse.swt.svg;
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/SVGRasterizer.java
user: login: akurtakov
body: License header is a must before even reading further.
created_at: 2024-12-11T07:11:07Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,27 @@
+package org.eclipse.swt.graphics;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
user: login: akurtakov
body: Missing license header.
created_at: 2024-12-11T07:11:44Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,38 @@
+package org.eclipse.swt.graphics;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGRasterizerRegistry.java
user: login: akurtakov
body: Missing license header.
created_at: 2024-12-11T07:15:17Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,27 @@
+package org.eclipse.swt.graphics;
+
+import java.awt.image.*;
+import java.io.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface ISVGRasterizer {
+
+	/**
+	 * Rasterizes an SVG image from the provided byte array, using the specified
+	 * zoom factor.
+	 *
+	 * @param bytes         the SVG image as a byte array.
+	 * @param scalingFactor the scaling ratio e.g. 2.0 for doubled size.
+	 * @return a {@link BufferedImage} containing the rasterized image, or
+	 *         {@code null} if the input is not a valid SVG file or cannot be
+	 *         processed.
+	 * @throws IOException if an error occurs while reading the SVG data.
+	 */
+	public BufferedImage rasterizeSVG(byte[] bytes, float scalingFactor) throws IOException;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
user: login: akurtakov
body: ImageData has different implementations but they all expose the same API so you don't have to choose, rather stick to the public API and SWT uses the implementation for the given OS.  What do you need FileFormat for ?
created_at: 2024-12-11T07:15:35Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,43 @@
+package org.eclipse.swt.graphics;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: akurtakov
body: Missing license header.
created_at: 2024-12-11T07:16:04Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,43 @@
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: akurtakov
body: I don't believe this is smth that SWT should expose as API thus it should be moved to internal package.
created_at: 2024-12-11T07:17:08Z
author_association: MEMBER
diff_hunk: @@ -149,10 +152,72 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    byte[] bytes = null;
+	try {
+		bytes = stream.readAllBytes();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	ISVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+	    try {
+	    	float scalingFactor = zoom / 100.0f;
+	    	BufferedImage image = rasterizer.rasterizeSVG(bytes, scalingFactor);
+	    	if(image != null) {
+	    		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+	        	    ImageIO.write(image, "png", baos);
+	        	    try (InputStream in = new ByteArrayInputStream(baos.toByteArray())) {
+	        	    	data = FileFormat.load(in, this);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: akurtakov
body: AWT is not a mandatory dependency to SWT and is loaded only by SWT_AWT (https://github.com/eclipse-platform/eclipse.platform.swt/tree/master/bundles/org.eclipse.swt/Eclipse%20SWT%20AWT ) and it is supposed to work by design even without java.desktop module as all functionality should be provided by SWT (wrapping OS and when needed providing small implementation itself). If/whether we want to change this is a bigger question which should not be done here.
created_at: 2024-12-11T07:19:41Z
author_association: MEMBER
diff_hunk: @@ -149,10 +152,72 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    byte[] bytes = null;
+	try {
+		bytes = stream.readAllBytes();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	ISVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+	    try {
+	    	float scalingFactor = zoom / 100.0f;
+	    	BufferedImage image = rasterizer.rasterizeSVG(bytes, scalingFactor);
+	    	if(image != null) {
+	    		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+	        	    ImageIO.write(image, "png", baos);
+	        	    try (InputStream in = new ByteArrayInputStream(baos.toByteArray())) {
+	        	    	data = FileFormat.load(in, this);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: laeubi
body: >  If/whether we want to change this is a bigger question which should not be done here.

pleas be aware of `import java.awt.*` in `SVGRasterizer` then ... so I think this of relates here, and should be done here.
created_at: 2024-12-11T07:28:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,43 @@
+package org.eclipse.swt.graphics;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: Michael5601
body: Done.
created_at: 2024-12-11T07:30:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,38 @@
+package org.eclipse.swt.graphics;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGRasterizerRegistry.java
user: login: Michael5601
body: Done.
created_at: 2024-12-11T07:30:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,27 @@
+package org.eclipse.swt.graphics;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
user: login: Michael5601
body: Done.
created_at: 2024-12-11T07:31:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,73 @@
+package org.eclipse.swt.svg;
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/SVGRasterizer.java
user: login: Michael5601
body: Yes I forgot it my bad. But now they are included.
created_at: 2024-12-11T07:32:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,10 +152,72 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    byte[] bytes = null;
+	try {
+		bytes = stream.readAllBytes();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	ISVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+	    try {
+	    	float scalingFactor = zoom / 100.0f;
+	    	BufferedImage image = rasterizer.rasterizeSVG(bytes, scalingFactor);
+	    	if(image != null) {
+	    		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+	        	    ImageIO.write(image, "png", baos);
+	        	    try (InputStream in = new ByteArrayInputStream(baos.toByteArray())) {
+	        	    	data = FileFormat.load(in, this);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: Michael5601
body: > > If/whether we want to change this is a bigger question which should not be done here.
> 
> pleas be aware of `import java.awt.*` in `SVGRasterizer` then ... so I think this of relates here, and should be done here.

I don't think we can get rid of the import in `SVGRasterizer` as JSVG works only with AWT afaik. 
created_at: 2024-12-11T07:35:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,43 @@
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: Michael5601
body: SVGRasterizer needs this utility and cannot use it if it's in an internal package. Before I had the utility methods in SVGRasterizer itself but I thought it would be better to move it into SWT. 
created_at: 2024-12-11T07:40:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,10 +152,72 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    byte[] bytes = null;
+	try {
+		bytes = stream.readAllBytes();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	ISVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+	    try {
+	    	float scalingFactor = zoom / 100.0f;
+	    	BufferedImage image = rasterizer.rasterizeSVG(bytes, scalingFactor);
+	    	if(image != null) {
+	    		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+	        	    ImageIO.write(image, "png", baos);
+	        	    try (InputStream in = new ByteArrayInputStream(baos.toByteArray())) {
+	        	    	data = FileFormat.load(in, this);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: laeubi
body: > I don't think we can get rid of the import in SVGRasterizer as JSVG works only with AWT afaik.

That's what I tried to say, AWT will be a requirement for SWT (SVG) after this change so it would be good to discuss this (possible) issue rater now than later as this was the reason why my previous work for bring BufferedImage>SWT Image into SWT was effectively rejected.

created_at: 2024-12-11T07:42:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,10 +152,72 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    byte[] bytes = null;
+	try {
+		bytes = stream.readAllBytes();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	ISVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+	    try {
+	    	float scalingFactor = zoom / 100.0f;
+	    	BufferedImage image = rasterizer.rasterizeSVG(bytes, scalingFactor);
+	    	if(image != null) {
+	    		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+	        	    ImageIO.write(image, "png", baos);
+	        	    try (InputStream in = new ByteArrayInputStream(baos.toByteArray())) {
+	        	    	data = FileFormat.load(in, this);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: akurtakov
body: You can use it in o.e.swt.svg but not in the other fragments/main swt project.
created_at: 2024-12-11T07:43:12Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.awt.image.*;
+import java.io.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface ISVGRasterizer {
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
user: login: laeubi
body: ```suggestion
public interface SVGRasterizer {
```
created_at: 2024-12-11T07:43:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.*;
+
+import java.awt.*;
+import java.awt.image.*;
+import java.io.*;
+import java.util.*;
+import org.eclipse.swt.graphics.ISVGRasterizer;
+import org.eclipse.swt.graphics.SVGRasterizerRegistry;
+import org.eclipse.swt.graphics.SVGUtil;
+
+import com.github.weisj.jsvg.*;
+import com.github.weisj.jsvg.geometry.size.*;
+import com.github.weisj.jsvg.parser.*;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class implements the {@code ISVGRasterizer} interface.
+ * 
+ * @since 1.0.0
+ */
+public class SVGRasterizer implements ISVGRasterizer {
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/SVGRasterizer.java
user: login: laeubi
body: ```suggestion
public class JSVGRasterizer implements SVGRasterizer {
```
created_at: 2024-12-11T07:43:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,43 @@
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: akurtakov
body: Just imagine SWT providing PNGUtil.isPNGFile, BMPUtil.isBMPFile, GifUtil.isGifFile and so on and on. From API POV this is clear no go.
created_at: 2024-12-11T07:44:50Z
author_association: MEMBER
diff_hunk: @@ -149,10 +152,72 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    byte[] bytes = null;
+	try {
+		bytes = stream.readAllBytes();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	ISVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+	    try {
+	    	float scalingFactor = zoom / 100.0f;
+	    	BufferedImage image = rasterizer.rasterizeSVG(bytes, scalingFactor);
+	    	if(image != null) {
+	    		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+	        	    ImageIO.write(image, "png", baos);
+	        	    try (InputStream in = new ByteArrayInputStream(baos.toByteArray())) {
+	        	    	data = FileFormat.load(in, this);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: akurtakov
body: AWT is fine to be requirement for org.eclipse.swt.svg fragment and whoever decides to depend/include  it. Main project should stick to SWT's Image* API.
created_at: 2024-12-11T07:48:50Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,27 @@
+package org.eclipse.swt.graphics;
+
+import java.awt.image.*;
+import java.io.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface ISVGRasterizer {
+
+	/**
+	 * Rasterizes an SVG image from the provided byte array, using the specified
+	 * zoom factor.
+	 *
+	 * @param bytes         the SVG image as a byte array.
+	 * @param scalingFactor the scaling ratio e.g. 2.0 for doubled size.
+	 * @return a {@link BufferedImage} containing the rasterized image, or
+	 *         {@code null} if the input is not a valid SVG file or cannot be
+	 *         processed.
+	 * @throws IOException if an error occurs while reading the SVG data.
+	 */
+	public BufferedImage rasterizeSVG(byte[] bytes, float scalingFactor) throws IOException;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
user: login: Michael5601
body: `ImageLoader` uses `FileFormat` to load `ImageData`. My PR does not change this behaviour right now. @DevCharly proposed that I could move the creation of `ImageData` into `SVGRasterizer` to avoid returning a `BufferedImage`. 

One way to do this would be to move the standard behaviour into `SVGRasterizer`. This would mean the loading with `FileFormat` would be moved to `SVGRasterizer`. 

An alternative is to use the approach @DevCharly also suggested in his second [comment](https://github.com/eclipse-platform/eclipse.platform.swt/pull/1638#discussion_r1878207440) by directly converting `BufferedImage` to `ImageData`. This would skip the need to use `FileFormat` for loading the new `InputStream`. 
created_at: 2024-12-11T07:50:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
+		String content = new String(data, 0, Math.min(data.length, 512), StandardCharsets.UTF_8);
+		return content.contains("<svg");
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: laeubi
body: This is wrong anyways, svg can have xml document declaration like this

```
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" ...>
```

For the sake of this check, simply check if the first byte is an `<` (what is invalid for any other image format to start with we currently support).

Also I think the rasterizer should not need to check this again, the caller is responsible to only pass valid data or will get an error.
created_at: 2024-12-11T07:51:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
+		String content = new String(data, 0, Math.min(data.length, 512), StandardCharsets.UTF_8);
+		return content.contains("<svg");
+	}
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param inputStream the input stream to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(InputStream inputStream) throws IOException {
+		if (inputStream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		byte[] data = inputStream.readNBytes(512);
+		return isSVGFile(data);
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: laeubi
body: This will corrupt the input stream, so it is quite useless method as afterwards the stream is unusable.

You should use mark / reset so the stream is in the same state as before (what would requires callers to use a BufferedStream).
created_at: 2024-12-11T07:52:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.awt.image.*;
+import java.io.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface ISVGRasterizer {
+
+	/**
+	 * Rasterizes an SVG image from the provided byte array, using the specified
+	 * zoom factor.
+	 *
+	 * @param bytes         the SVG image as a byte array.
+	 * @param scalingFactor the scaling ratio e.g. 2.0 for doubled size.
+	 * @return a {@link BufferedImage} containing the rasterized image, or
+	 *         {@code null} if the input is not a valid SVG file or cannot be
+	 *         processed.
+	 * @throws IOException if an error occurs while reading the SVG data.
+	 */
+	public BufferedImage rasterizeSVG(byte[] bytes, float scalingFactor) throws IOException;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
user: login: laeubi
body: ```suggestion
	public BufferedImage rasterizeSVG(InputStream stream, float scalingFactor) throws IOException;
```

or even better

```suggestion
	public BufferedImage rasterizeSVG(URL url, float scalingFactor) throws IOException;
```
created_at: 2024-12-11T07:53:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,10 +152,72 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    byte[] bytes = null;
+	try {
+		bytes = stream.readAllBytes();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	ISVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+	    try {
+	    	float scalingFactor = zoom / 100.0f;
+	    	BufferedImage image = rasterizer.rasterizeSVG(bytes, scalingFactor);
+	    	if(image != null) {
+	    		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+	        	    ImageIO.write(image, "png", baos);
+	        	    try (InputStream in = new ByteArrayInputStream(baos.toByteArray())) {
+	        	    	data = FileFormat.load(in, this);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: Michael5601
body: > AWT is fine to be requirement for org.eclipse.swt.svg fragment and whoever decides to depend/include it. Main project should stick to SWT's Image* API.

Now I understand. So the proposed solution from @DevCharly would be better as we could avoid AWT in SWT. 
created_at: 2024-12-11T07:55:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.awt.image.*;
+import java.io.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface ISVGRasterizer {
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
user: login: Michael5601
body: changed in ffa17e4b4f65481d700b9d7fc27c305934166080.
created_at: 2024-12-11T08:06:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.*;
+
+import java.awt.*;
+import java.awt.image.*;
+import java.io.*;
+import java.util.*;
+import org.eclipse.swt.graphics.ISVGRasterizer;
+import org.eclipse.swt.graphics.SVGRasterizerRegistry;
+import org.eclipse.swt.graphics.SVGUtil;
+
+import com.github.weisj.jsvg.*;
+import com.github.weisj.jsvg.geometry.size.*;
+import com.github.weisj.jsvg.parser.*;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class implements the {@code ISVGRasterizer} interface.
+ * 
+ * @since 1.0.0
+ */
+public class SVGRasterizer implements ISVGRasterizer {
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/SVGRasterizer.java
user: login: Michael5601
body: changed in ffa17e4b4f65481d700b9d7fc27c305934166080.
created_at: 2024-12-11T08:07:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,10 +152,72 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    byte[] bytes = null;
+	try {
+		bytes = stream.readAllBytes();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	ISVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+	    try {
+	    	float scalingFactor = zoom / 100.0f;
+	    	BufferedImage image = rasterizer.rasterizeSVG(bytes, scalingFactor);
+	    	if(image != null) {
+	    		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+	        	    ImageIO.write(image, "png", baos);
+	        	    try (InputStream in = new ByteArrayInputStream(baos.toByteArray())) {
+	        	    	data = FileFormat.load(in, this);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: Michael5601
body: I changed this in ffa17e4b4f65481d700b9d7fc27c305934166080. Now AWT is only used by the new plugin in the class `JSVGRasterizer` (former `SVGRasterizer`).
created_at: 2024-12-11T08:09:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,27 @@
+package org.eclipse.swt.graphics;
+
+import java.awt.image.*;
+import java.io.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface ISVGRasterizer {
+
+	/**
+	 * Rasterizes an SVG image from the provided byte array, using the specified
+	 * zoom factor.
+	 *
+	 * @param bytes         the SVG image as a byte array.
+	 * @param scalingFactor the scaling ratio e.g. 2.0 for doubled size.
+	 * @return a {@link BufferedImage} containing the rasterized image, or
+	 *         {@code null} if the input is not a valid SVG file or cannot be
+	 *         processed.
+	 * @throws IOException if an error occurs while reading the SVG data.
+	 */
+	public BufferedImage rasterizeSVG(byte[] bytes, float scalingFactor) throws IOException;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
user: login: Michael5601
body: I changed this in ffa17e4b4f65481d700b9d7fc27c305934166080 like @DevCharly proposed as it fixed the conversion issue and AWT does not need to be used in SWT. 
created_at: 2024-12-11T08:13:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,43 @@
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: Michael5601
body: I agree. Changed in 9648549f9e14e8cf1f8538949392162a42aa173f. I moved the utility methods back into the `JSVGRasterizer` (former `SVGRasterizer`). I also deleted the class `SVGUtil` as it was empty after the change. 
created_at: 2024-12-11T08:20:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
+		String content = new String(data, 0, Math.min(data.length, 512), StandardCharsets.UTF_8);
+		return content.contains("<svg");
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: Michael5601
body: Thank you for the remark, I will fix it. But can you elaborate why this shouldn't work with the snippet you provided? The `<svg` tag in the snippet would be read by my code.
created_at: 2024-12-11T08:23:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
+		String content = new String(data, 0, Math.min(data.length, 512), StandardCharsets.UTF_8);
+		return content.contains("<svg");
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: Michael5601
body: This is changed in 3bc362193af1f1918abbf985346dee5f39de997d. The caller is now responsible for making sure the InputStream contains an SVGFile. Also only the first byte is checked. 
created_at: 2024-12-11T08:52:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
+		String content = new String(data, 0, Math.min(data.length, 512), StandardCharsets.UTF_8);
+		return content.contains("<svg");
+	}
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param inputStream the input stream to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(InputStream inputStream) throws IOException {
+		if (inputStream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		byte[] data = inputStream.readNBytes(512);
+		return isSVGFile(data);
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: Michael5601
body: > You should use mark / reset so the stream is in the same state as before (what would requires callers to use a BufferedStream).

If the callers would use a `BufferedStream` I would have written the code different but they don't. This is why I need to create a byte[] array and then always create a new `ByteArrayInputStream` from it. 
I searched for a way to solve this differently but wasn't successful. If someone has an idea I would be grateful. 
created_at: 2024-12-11T08:56:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
+		String content = new String(data, 0, Math.min(data.length, 512), StandardCharsets.UTF_8);
+		return content.contains("<svg");
+	}
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param inputStream the input stream to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(InputStream inputStream) throws IOException {
+		if (inputStream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		byte[] data = inputStream.readNBytes(512);
+		return isSVGFile(data);
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: laeubi
body: The caller is SWT code so this can be made work for sure ;-)

Otherwise as mentioned I don't see any benefit in providing a stream variant at all if the stream is unusable after that.
created_at: 2024-12-11T09:01:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.awt.image.*;
+import java.io.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface ISVGRasterizer {
+
+	/**
+	 * Rasterizes an SVG image from the provided byte array, using the specified
+	 * zoom factor.
+	 *
+	 * @param bytes         the SVG image as a byte array.
+	 * @param scalingFactor the scaling ratio e.g. 2.0 for doubled size.
+	 * @return a {@link BufferedImage} containing the rasterized image, or
+	 *         {@code null} if the input is not a valid SVG file or cannot be
+	 *         processed.
+	 * @throws IOException if an error occurs while reading the SVG data.
+	 */
+	public BufferedImage rasterizeSVG(byte[] bytes, float scalingFactor) throws IOException;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ISVGRasterizer.java
user: login: Michael5601
body: As the SVG-File check does not happen inside of JSVGRasterizer anymore I could change the code so that `rasterizeSVG` does accept an `InputStream` instead of a byte[] array. This is changed in 703913e67a9698227ed6ba4fdd2c54677d96cfba.

I don't see how this could be done with URL without big changes in the callers. 
created_at: 2024-12-11T09:11:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
+		String content = new String(data, 0, Math.min(data.length, 512), StandardCharsets.UTF_8);
+		return content.contains("<svg");
+	}
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param inputStream the input stream to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(InputStream inputStream) throws IOException {
+		if (inputStream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		byte[] data = inputStream.readNBytes(512);
+		return isSVGFile(data);
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: Michael5601
body: The streams come from two sources: 
- For Standard-Icons or graphically customized icons these are always FileInputStreams that can be wrapped to a BufferedInputStream. 
- For Composite-Icons they are already BufferedInputStreams. 

The only problem that I have while testing is that the `SVGLoader.load` method of JSVG closes the stream which prevents a reset after the call. @weisJ do you know why this happens?
created_at: 2024-12-11T10:12:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
+		String content = new String(data, 0, Math.min(data.length, 512), StandardCharsets.UTF_8);
+		return content.contains("<svg");
+	}
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param inputStream the input stream to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(InputStream inputStream) throws IOException {
+		if (inputStream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		byte[] data = inputStream.readNBytes(512);
+		return isSVGFile(data);
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: weisJ
body: JSVG wraps the given InputStream, hence also closes it. This might close the wrapped stream as well.
created_at: 2024-12-11T12:54:54Z
author_association: NONE
diff_hunk: @@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
+		String content = new String(data, 0, Math.min(data.length, 512), StandardCharsets.UTF_8);
+		return content.contains("<svg");
+	}
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param inputStream the input stream to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(InputStream inputStream) throws IOException {
+		if (inputStream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		byte[] data = inputStream.readNBytes(512);
+		return isSVGFile(data);
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: Michael5601
body: > JSVG wraps the given InputStream, hence also closes it. This might close the wrapped stream as well.

yes it does. I will try to find another solution to this. 
created_at: 2024-12-11T14:11:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
+		String content = new String(data, 0, Math.min(data.length, 512), StandardCharsets.UTF_8);
+		return content.contains("<svg");
+	}
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param inputStream the input stream to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(InputStream inputStream) throws IOException {
+		if (inputStream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		byte[] data = inputStream.readNBytes(512);
+		return isSVGFile(data);
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: laeubi
body: Just wrap your stream in a `FilteredInputStream` and override close method to do nothing.
created_at: 2024-12-11T14:23:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: SvgPlugin
+Bundle-SymbolicName: org.eclipse.swt.svg
+Bundle-Version: 1.0.0.qualifier
+Automatic-Module-Name: org.eclipse.swt.svgPlugin
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Export-Package: org.eclipse.swt.svg
+Import-Package: org.eclipse.swt.graphics
path: bundles/org.eclipse.swt.svg/META-INF/MANIFEST.MF
user: login: HannesWell
body: ```suggestion
Fragment-Host: org.eclipse.swt
Export-Package: org.eclipse.swt.svg
```
created_at: 2024-12-11T21:52:48Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="lib" path="libs/jsvg-1.6.1.jar"/>
path: bundles/org.eclipse.swt.svg/.classpath
user: login: HannesWell
body: ```suggestion
	<classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.swt.win32.win32.x86_64" />
	<classpathentry kind="lib" path="libs/jsvg-1.6.1.jar"/>
```
This makes this project on o.e.swt.win32.win32.x86_64 and adds the latter to the classpath of the former, similar like how it's happening at runtime.

Of course this now only works for win32.x86_64. I also added other swt-fragments, but it looks like this is not optional and on a first sight I didn't found a way to make it optional. But it is possible to turn incomplete-classpath errors into warnings by adding `org.eclipse.jdt.core.incompleteClasspath=warning` to `.settings/org.eclipse.jdt.core.prefs`.
And to make it work in the build it is hopefully sufficient to add a pom.xml to the project where, depending on the running platform, an explicit dependency to the corresponding native fragment is added.

created_at: 2024-12-11T22:05:25Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface SVGRasterizer {
+
+	/**
+	 * Rasterizes an SVG image from the provided byte array, using the specified
+	 * zoom factor.
+	 *
+	 * @param stream        the SVG image as an {@link InputStream}.
+	 * @param scalingFactor the scaling ratio e.g. 2.0 for doubled size.
+	 * @return the {@link ImageData} for the rasterized image, or
+	 *         {@code null} if the input is not a valid SVG file or cannot be
+	 *         processed.
+	 * @throws IOException if an error occurs while reading the SVG data.
+	 */
+	public ImageData rasterizeSVG(InputStream stream, float scalingFactor) throws IOException;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGRasterizer.java
user: login: HannesWell
body: This method could return an optional, which would save us the separate `isSVGFile()` method.
```suggestion
	public Optional<ImageData> rasterizeSVG(InputStream stream, float scalingFactor) throws IOException;
```

Then, from a functionality point of view, this interface could be replaced by a 
`BiFunction<InputStream, Float, Optional<ImageData>>`
created_at: 2024-12-11T22:45:26Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
user: login: HannesWell
body: It's 2024 already (here and below) :) And even 2025 soon 😨
```suggestion
 * Copyright (c) 2024 Vector Informatik GmbH and others.
```
created_at: 2024-12-11T22:46:21Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
user: login: HannesWell
body: If you want to, you can also use your name in the contributors list, like for example:
```suggestion
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
```

created_at: 2024-12-11T22:49:55Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
+		String content = new String(data, 0, Math.min(data.length, 512), StandardCharsets.UTF_8);
+		return content.contains("<svg");
+	}
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param inputStream the input stream to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(InputStream inputStream) throws IOException {
+		if (inputStream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		byte[] data = inputStream.readNBytes(512);
+		return isSVGFile(data);
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: HannesWell
body: > I searched for a way to solve this differently but wasn't successful. If someone has an idea I would be grateful.

Using a `PushbackInputStream` could probably help here and you could still consider streams that support marks:
```
	if (stream.markSupported()) {
		stream.mark(1);
		int firstByte = stream.read();
		if (firstByte != '<') {
			// TODO: handle not an SVG, maybe return
		}
		stream.reset();
	} else {
		PushbackInputStream pushbackStream=new PushbackInputStream(stream,1);
		int firstByte = stream.read();
		if (firstByte != '<') {
			// TODO: handle not an SVG, maybe return
		}
		pushbackStream.unread(firstByte);
		stream = pushbackStream;
	}
	// ... rasterize the SVG
```
created_at: 2024-12-11T23:03:43Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
+		String content = new String(data, 0, Math.min(data.length, 512), StandardCharsets.UTF_8);
+		return content.contains("<svg");
+	}
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param inputStream the input stream to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(InputStream inputStream) throws IOException {
+		if (inputStream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		byte[] data = inputStream.readNBytes(512);
+		return isSVGFile(data);
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: HannesWell
body: Or yes, it could also be sufficient to just wrap the given `InputStream` into a `BufferedInputStream` if mark `markSupported()` returns false, before it is passed to a method that might read a few bytes to test if the content is a SVG.
created_at: 2024-12-11T23:13:30Z
author_association: MEMBER
diff_hunk: @@ -149,10 +149,71 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    byte[] bytes = null;
+	try {
+		bytes = stream.readAllBytes();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+		try (InputStream imageStream = new ByteArrayInputStream(bytes)) {
+			if (rasterizer.isSVGFile(imageStream)) {
+				float scalingFactor = zoom / 100.0f;
+				try (InputStream svgFileStream = new ByteArrayInputStream(bytes)) {
+					ImageData rasterizedData = rasterizer.rasterizeSVG(svgFileStream, scalingFactor);
+					if (rasterizedData != null) {
+						data = new ImageData[]{rasterizedData};
+					    return data;
+					}
+				}
+			}
+		} catch (IOException e) {
+			//ignore.
+		}
+	}
+	try (InputStream fallbackStream = new ByteArrayInputStream(bytes)) {
+		return loadDefault(fallbackStream);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	return null;
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: HannesWell
body: With https://github.com/eclipse-platform/eclipse.platform.swt/pull/1638#discussion_r1881143635 and https://github.com/eclipse-platform/eclipse.platform.swt/pull/1638#discussion_r1881114672, you could avoid reading the entire file-content into a byte-array and just wrap `stream` it into a `BufferedInputStream` if marking is not supported and assuming that the rasterizeSVG() will reset the stream if it has detected that the content isn't an SVG:
```suggestion
	SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
	if (rasterizer != null && zoom != 0) {
		if (!stream.markSupported()) {
			stream = new BufferedStream(stream);
		}
		float scalingFactor = zoom / 100.0f;
		Optional<ImageData> rasterizedData = rasterizer.rasterizeSVG(stream, scalingFactor);
		if (rasterizedData.isPresent()) {
			data = new ImageData[]{rasterizedData.get()};
			return data;
		}
	}
	try {
		return loadDefault(stream);
	} catch (IOException e) {
		SWT.error(SWT.ERROR_IO, e);
	}
	return null;
```

This also takes into account, that in general the creator of a stream is also responsible to close it, not the receivers.
created_at: 2024-12-11T23:21:57Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
+		String content = new String(data, 0, Math.min(data.length, 512), StandardCharsets.UTF_8);
+		return content.contains("<svg");
+	}
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param inputStream the input stream to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(InputStream inputStream) throws IOException {
+		if (inputStream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		byte[] data = inputStream.readNBytes(512);
+		return isSVGFile(data);
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: laeubi
body: @HannesWell this will only work if you "own" the stream, but I though the problem was more that JSVG closes the stream.

In anyway we should not complicate this too much this is not something I would assume to become a popular API with many callers, so we can simply make some assertions / requirements (e.g if one want to be explicitly require a `PushbackInputStream`).
created_at: 2024-12-12T07:01:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
user: login: Michael5601
body: Thanks for the hint. :) Fixed in 1ccfe6580fff2145e1c59987649dddb8b1fe0ed8
created_at: 2024-12-12T09:33:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
user: login: Michael5601
body: Fixed in 1ccfe6580fff2145e1c59987649dddb8b1fe0ed8.
created_at: 2024-12-12T09:33:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface SVGRasterizer {
+
+	/**
+	 * Rasterizes an SVG image from the provided byte array, using the specified
+	 * zoom factor.
+	 *
+	 * @param stream        the SVG image as an {@link InputStream}.
+	 * @param scalingFactor the scaling ratio e.g. 2.0 for doubled size.
+	 * @return the {@link ImageData} for the rasterized image, or
+	 *         {@code null} if the input is not a valid SVG file or cannot be
+	 *         processed.
+	 * @throws IOException if an error occurs while reading the SVG data.
+	 */
+	public ImageData rasterizeSVG(InputStream stream, float scalingFactor) throws IOException;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGRasterizer.java
user: login: Michael5601
body: The `isSVGFile()` is also used in Jface (see my [Platform UI PR](https://github.com/eclipse-platform/eclipse.platform.ui/pull/2593)) so I can't delete it. 
Additionally `svgDocument = svgLoader.load(stream, null, LoaderContext.createDefault());` inside of `JSVGRasterizer` prints an exception in the console if it is called with a non-SVG-InputStream. I need to check this before calling the method. This is why I can't use Optional here.
created_at: 2024-12-12T09:50:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,10 +149,71 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    byte[] bytes = null;
+	try {
+		bytes = stream.readAllBytes();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+		try (InputStream imageStream = new ByteArrayInputStream(bytes)) {
+			if (rasterizer.isSVGFile(imageStream)) {
+				float scalingFactor = zoom / 100.0f;
+				try (InputStream svgFileStream = new ByteArrayInputStream(bytes)) {
+					ImageData rasterizedData = rasterizer.rasterizeSVG(svgFileStream, scalingFactor);
+					if (rasterizedData != null) {
+						data = new ImageData[]{rasterizedData};
+					    return data;
+					}
+				}
+			}
+		} catch (IOException e) {
+			//ignore.
+		}
+	}
+	try (InputStream fallbackStream = new ByteArrayInputStream(bytes)) {
+		return loadDefault(fallbackStream);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	return null;
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: Michael5601
body: This does not fix the problem that JSVG closes the InputStream as @laeubi explained [here](https://github.com/eclipse-platform/eclipse.platform.swt/pull/1638#discussion_r1881488576). I could use an `BufferedInputStream` for `isSVGFile()` but not for `rasterizeSVG()`. The solution to override the `close()` method in a `FilterInputStream` wrap worked for me. See b5365d82069f08efd7ae04da048a65ea2271b1ec.
created_at: 2024-12-12T09:56:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface SVGRasterizer {
+
+	/**
+	 * Rasterizes an SVG image from the provided byte array, using the specified
+	 * zoom factor.
+	 *
+	 * @param stream        the SVG image as an {@link InputStream}.
+	 * @param scalingFactor the scaling ratio e.g. 2.0 for doubled size.
+	 * @return the {@link ImageData} for the rasterized image, or
+	 *         {@code null} if the input is not a valid SVG file or cannot be
+	 *         processed.
+	 * @throws IOException if an error occurs while reading the SVG data.
+	 */
+	public ImageData rasterizeSVG(InputStream stream, float scalingFactor) throws IOException;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGRasterizer.java
user: login: laeubi
body: Please note that even code-share is good, **sometimes** it is better to duplicate a small trivial code block instead of creating a link between two modules.

That said, in this case I think it is more suitable to have the same method once in SWT and once in JFace instead of making it a public API we need to maintain.
created_at: 2024-12-12T10:14:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+import java.nio.charset.*;
+
+/**
+ * Utility class for handling SVG-related operations.
+ *
+ * @since 3.129
+ */
+public class SVGUtil {
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param data byte array to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(byte[] data) throws IOException {
+		String content = new String(data, 0, Math.min(data.length, 512), StandardCharsets.UTF_8);
+		return content.contains("<svg");
+	}
+
+	/**
+	 * Determines whether the given {@link InputStream} contains a SVG file.
+	 *
+	 * @param inputStream the input stream to check.
+	 * @return {@code true} if the input stream contains SVG content; {@code false}
+	 *         otherwise.
+	 * @throws IOException              if an error occurs while reading the stream.
+	 * @throws IllegalArgumentException if the input stream is {@code null}.
+	 */
+	public static boolean isSVGFile(InputStream inputStream) throws IOException {
+		if (inputStream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		byte[] data = inputStream.readNBytes(512);
+		return isSVGFile(data);
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGUtil.java
user: login: Michael5601
body: > Just wrap your stream in a `FilteredInputStream` and override close method to do nothing.

This worked for me. See b5365d82069f08efd7ae04da048a65ea2271b1ec for the commit.
created_at: 2024-12-12T10:25:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Vector Informatik GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface SVGRasterizer {
+
+	/**
+	 * Rasterizes an SVG image from the provided byte array, using the specified
+	 * zoom factor.
+	 *
+	 * @param stream        the SVG image as an {@link InputStream}.
+	 * @param scalingFactor the scaling ratio e.g. 2.0 for doubled size.
+	 * @return the {@link ImageData} for the rasterized image, or
+	 *         {@code null} if the input is not a valid SVG file or cannot be
+	 *         processed.
+	 * @throws IOException if an error occurs while reading the SVG data.
+	 */
+	public ImageData rasterizeSVG(InputStream stream, float scalingFactor) throws IOException;
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGRasterizer.java
user: login: Michael5601
body: I see your point. Right now the method is public and inside of `JSVGRasterizer` in the new plugin. Your suggestion is to make the method private and move it into `URLImageDescriptor` so it can be used in the two places there. Additionally it should be duplicated and placed into `ImageLoader` so it can be used in the `load` method. 

I don't have a stong opinion about this. @HannesWell what do you think?
created_at: 2024-12-12T10:38:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="lib" path="libs/jsvg-1.6.1.jar"/>
path: bundles/org.eclipse.swt.svg/.classpath
user: login: Michael5601
body: I have to admit that I am not very experienced with this topic. It seems you have an idea how this could work with fragments if I read it right. This would be very nice. It would be great if we could have a call where you can explain this approach to me. 
created_at: 2024-12-12T13:47:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="lib" path="libs/jsvg-1.6.1.jar"/>
path: bundles/org.eclipse.swt.svg/.classpath
user: login: HannesWell
body: I just pushed the changes made for/in our 1:1 call yesterday. Feel free to in-cooperate/squash them into your other commits.
I also added a few todos of things that could be done further.
created_at: 2024-12-17T21:37:04Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="lib" path="libs/jsvg-1.6.1.jar"/>
path: bundles/org.eclipse.swt.svg/.classpath
user: login: Michael5601
body: Thank you again for your help. It is way better now. :)
I will try to delete the isSVG-Method as you suggested. 
created_at: 2024-12-18T10:34:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="lib" path="libs/jsvg-1.6.1.jar"/>
path: bundles/org.eclipse.swt.svg/.classpath
user: login: Michael5601
body: I forgot to mention in our call that I still can't see a way to prevent JSVG throwing an error if `SVGLoader.load()` is called with a png. This is why the isSVG() check is needed before the call. I also can't check for the file ending as I only have the InputStream.  

It still works though but it throws an error every time.
created_at: 2024-12-18T10:48:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,10 +149,61 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    if (!stream.markSupported()) {
+		stream = new BufferedInputStream(stream);
+	}
+	SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+		try {
+			if (rasterizer.isSVGFile(stream)) {
+				float scalingFactor = zoom / 100.0f;
+				ImageData rasterizedData = rasterizer.rasterizeSVG(stream, scalingFactor);
+				if (rasterizedData != null) {
+					data = new ImageData[]{rasterizedData};
+				    return data;
+				}
+			}
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: HannesWell
body: ```suggestion
			SWT.error(SWT.ERROR_INVALID_IMAGE, e);
```
created_at: 2025-01-19T12:41:16Z
author_association: MEMBER
diff_hunk: @@ -159,11 +162,61 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    if (!stream.markSupported()) {
+		stream = new BufferedInputStream(stream);
+	}
+	SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+		try {
+			if (rasterizer.isSVGFile(stream)) {
+				float scalingFactor = zoom / 100.0f;
+				ImageData rasterizedData = rasterizer.rasterizeSVG(stream, scalingFactor);
+				if (rasterizedData != null) {
+					data = new ImageData[]{rasterizedData};
+				    return data;
+				}
+			}
+		} catch (IOException e) {
+			//ignore.
path: bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java
user: login: HannesWell
body: Is there a specific reason why for Linux and mac this error is ignored, while it is propagated in Windows?
Usually it's better to not sallow exceptions/errors as it makes it harder to become aware of them and understand them.
created_at: 2025-01-19T12:42:38Z
author_association: MEMBER
diff_hunk: @@ -149,10 +149,61 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    if (!stream.markSupported()) {
+		stream = new BufferedInputStream(stream);
+	}
+	SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+		try {
+			if (rasterizer.isSVGFile(stream)) {
+				float scalingFactor = zoom / 100.0f;
+				ImageData rasterizedData = rasterizer.rasterizeSVG(stream, scalingFactor);
+				if (rasterizedData != null) {
+					data = new ImageData[]{rasterizedData};
+				    return data;
+				}
+			}
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		}
+	}
+	return loadDefault(stream);
+}
+
+private ImageData[] loadDefault(InputStream stream) {
 	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
 	reset();
 	data = FileFormat.load(stream, this);
-	return data;
+    return data;
 }
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: HannesWell
body: Introducing a private `loadDefault()` method seems not to be necessary. Keeping it as `load(InputStream)` and calling it from the other methods that currently call `loadDefault()` too, is fine and reduces the number of changes.

The same applies for the Linux and Mac implementation.

created_at: 2025-01-19T12:50:08Z
author_association: MEMBER
diff_hunk: @@ -291,18 +344,43 @@ boolean isInterlacedPNG(byte [] imageAsByteArray) {
  */
 public ImageData[] load(String filename) {
 	if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
-	InputStream stream = null;
-	try {
-		stream = new FileInputStream(filename);
-		return load(stream);
path: bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java
user: login: HannesWell
body: Please submit the simplification of the existing `ImageData[] load(String filename)` method (i.e. using try-with-resources in Linux and Mac, Windows seems already cleaned-up) as a separate PR.
This way we can reduce the number of not directly related changes in this PR.
created_at: 2025-01-19T12:52:51Z
author_association: MEMBER
diff_hunk: @@ -331,7 +331,39 @@ scanlinePad, checkData(data), 0, null,
  * @see ImageLoader#load(InputStream)
  */
 public ImageData(InputStream stream) {
-	ImageData[] data = ImageDataLoader.load(stream);
+	this(stream, 0);
+}
+
+/**
+ * Constructs an <code>ImageData</code> loaded from the specified
+ * input stream. Throws an error if an error occurs while loading
+ * the image, or if the image has an unsupported type.  Application
+ * code is still responsible for closing the input stream.
+ * <p>
+ * This constructor is provided for convenience when loading a single
+ * image only. If the stream contains multiple images, only the first
+ * one will be loaded. To load multiple images, use
+ * <code>ImageLoader.load()</code>.
+ * </p>
+ *
+ * @param stream the input stream to load the image from (must not be null)
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageData.java
user: login: HannesWell
body: The documentation about the values of the `zoom-factor` should be enhanced for the non-zero cases.
Currently it's not clear what values are expected. From the called code I assume it's value are integer percentage values.
created_at: 2025-01-19T13:04:28Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.*;
+
+import java.awt.*;
+import java.awt.image.*;
+import java.io.*;
+import java.util.*;
+import org.eclipse.swt.graphics.SVGRasterizer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.graphics.RGB;
+import com.github.weisj.jsvg.*;
+import com.github.weisj.jsvg.geometry.size.*;
+import com.github.weisj.jsvg.parser.*;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class implements the {@code ISVGRasterizer} interface.
+ * 
+ * @since 1.0.0
+ */
+public class JSVGRasterizer implements SVGRasterizer {
+	
+	private SVGLoader svgLoader;
+
+	private final static Map<Key, Object> RENDERING_HINTS = Map.of(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON, //
+			KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY, //
+			KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY, //
+			KEY_DITHERING, VALUE_DITHER_DISABLE, //
+			KEY_FRACTIONALMETRICS, VALUE_FRACTIONALMETRICS_ON, //
+			KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC, //
+			KEY_RENDERING, VALUE_RENDER_QUALITY, //
+			KEY_STROKE_CONTROL, VALUE_STROKE_PURE, //
+			KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON //
+	);
+
+	@Override
+	public ImageData rasterizeSVG(InputStream stream, float scalingFactor) throws IOException {
+		if (stream == null) {
+	        throw new IllegalArgumentException("InputStream cannot be null");
+	    }
+		stream.mark(Integer.MAX_VALUE);
+		if(svgLoader == null) {
+			svgLoader = new SVGLoader();
+		}
+		SVGDocument svgDocument = null;
+		InputStream nonClosingStream = new FilterInputStream(stream) {
+		        @Override
+		        public void close() throws IOException {
+		            // Do nothing to prevent closing the underlying stream
+		        }
+		    };
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
user: login: HannesWell
body: While I agree that it isn't good style from JSVG to close the stream, usually exactly that code that opens a stream is responsible for closing it, I wonder if it's still necessary to workaround this here?
At this point we are sure that the passed input stream denotes an SVG and the stream will be exhausted after the call any ways (or an exception is thrown because the stream content is invalid).
The same question applies to marking and reseting the stream in this method.
Doing this probably doesn't improve the performance (but I can only guess if it's a significant performance penalty).
created_at: 2025-01-19T13:54:12Z
author_association: MEMBER
diff_hunk: @@ -149,10 +149,61 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    if (!stream.markSupported()) {
+		stream = new BufferedInputStream(stream);
+	}
+	SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+		try {
+			if (rasterizer.isSVGFile(stream)) {
+				float scalingFactor = zoom / 100.0f;
+				ImageData rasterizedData = rasterizer.rasterizeSVG(stream, scalingFactor);
+				if (rasterizedData != null) {
+					data = new ImageData[]{rasterizedData};
+				    return data;
+				}
+			}
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: HannesWell
body: Instead of having to duplicated the code that eventually calls the `rasterizeSVG()` method duplicated in the three native implementations with all the special requirements (like mark-support) checked three times, I think it would be better to move it into a static method that takes care of everything and just returns an `ImageData`-array and handles the SVG and non SVG-case. Then we have all the logic encapsulated at one place (similar to `FileFormat` called below). The static method could for example be at `SVGRasterizer`:
```
public static ImageData[] rasterizeSVG(InputStream stream, int zoom, ImageLoader) {
 // all the code from current ImageLoader.load(InputStream stream, int zoom)
}
```
Then we could also avoid the separate `SVGRasterizer.isSVGFile()` method.

But thinking about it a bit more we could also move this to the `org.eclipse.swt.internal.image.FileFormat` class and just add that into a new `ImageData[] load(InputStream is, int zoom ImageLoader loader)` method.
There we could also inline the still relevant code of the `SVGRasterizerRegistry`.

created_at: 2025-01-19T14:12:36Z
author_association: MEMBER
diff_hunk: @@ -149,10 +149,61 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    if (!stream.markSupported()) {
+		stream = new BufferedInputStream(stream);
+	}
+	SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+		try {
+			if (rasterizer.isSVGFile(stream)) {
+				float scalingFactor = zoom / 100.0f;
+				ImageData rasterizedData = rasterizer.rasterizeSVG(stream, scalingFactor);
+				if (rasterizedData != null) {
+					data = new ImageData[]{rasterizedData};
+				    return data;
+				}
+			}
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: Michael5601
body: I followed your second approach and moved part of the code from `ImageLoader.load(InputStream stream, int zoom)` to a new method `FileFormat.load(InputStream stream, int zoom, ImageLoader loader)` and another part of the code into `rasterizeSVG()`. The method `isSVGFile()` can now be private. 

I don't agree with you on moving the non-SVG-case into the Rasterizer as using the rasterizer means loading the optional fragment with the JSVG dependency. If the SVG-case and the non-SVG-case are handled in the rasterizer, the fragment would be mandatory in every case. Please correct me if I am wrong. 
created_at: 2025-01-21T18:30:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -291,18 +344,43 @@ boolean isInterlacedPNG(byte [] imageAsByteArray) {
  */
 public ImageData[] load(String filename) {
 	if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
-	InputStream stream = null;
-	try {
-		stream = new FileInputStream(filename);
-		return load(stream);
path: bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java
user: login: Michael5601
body: I removed the implementation for other OS than windows and will provide a seperate PR for cocoa and gtk as they also need changes for the Image and ImageData classes.
created_at: 2025-01-21T18:31:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,10 +149,61 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    if (!stream.markSupported()) {
+		stream = new BufferedInputStream(stream);
+	}
+	SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+		try {
+			if (rasterizer.isSVGFile(stream)) {
+				float scalingFactor = zoom / 100.0f;
+				ImageData rasterizedData = rasterizer.rasterizeSVG(stream, scalingFactor);
+				if (rasterizedData != null) {
+					data = new ImageData[]{rasterizedData};
+				    return data;
+				}
+			}
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		}
+	}
+	return loadDefault(stream);
+}
+
+private ImageData[] loadDefault(InputStream stream) {
 	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
 	reset();
 	data = FileFormat.load(stream, this);
-	return data;
+    return data;
 }
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: Michael5601
body: I deleted the method `loadDefault()`. Thanks for your reviews :) Tomorrow I will address the rest of the reviews. 
created_at: 2025-01-21T18:35:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,11 +162,61 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    if (!stream.markSupported()) {
+		stream = new BufferedInputStream(stream);
+	}
+	SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+		try {
+			if (rasterizer.isSVGFile(stream)) {
+				float scalingFactor = zoom / 100.0f;
+				ImageData rasterizedData = rasterizer.rasterizeSVG(stream, scalingFactor);
+				if (rasterizedData != null) {
+					data = new ImageData[]{rasterizedData};
+				    return data;
+				}
+			}
+		} catch (IOException e) {
+			//ignore.
path: bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java
user: login: Michael5601
body: I ignored changing GTK and Cocoa as soon as I found out that they also need changes for the `Image` and `ImageData` class. For windows I improved the handling of the exception later on which is why it is different in the other OS.

I will provide the code for GTK and Cocoa as soon as I find some time. The last weeks my time was limited due to the final phase of my thesis. But it's finished soon and I am looking forward to finishing this project. :)
created_at: 2025-01-21T18:38:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,10 +149,61 @@ void reset() {
  * </ul>
  */
 public ImageData[] load(InputStream stream) {
+	return loadDefault(stream);
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    if (!stream.markSupported()) {
+		stream = new BufferedInputStream(stream);
+	}
+	SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+	if (rasterizer != null && zoom != 0) {
+		try {
+			if (rasterizer.isSVGFile(stream)) {
+				float scalingFactor = zoom / 100.0f;
+				ImageData rasterizedData = rasterizer.rasterizeSVG(stream, scalingFactor);
+				if (rasterizedData != null) {
+					data = new ImageData[]{rasterizedData};
+				    return data;
+				}
+			}
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: Michael5601
body: done.
created_at: 2025-01-21T18:39:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -155,6 +155,39 @@ public ImageData[] load(InputStream stream) {
 	return data;
 }
 
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    data = FileFormat.load(stream, zoom, this);
+    if (data == null) {
+    	load(stream);
+    }
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: HannesWell
body: This should be moved into the new `FileFormat.load(InputStream, int, ImageLoader)` method (that's why this is passed :) ) and it should be called if within `FileFormat.load()` if it is detected that the passed stream doesn't have SVG content.
This way we can handle the marking and potential going back in the stream in that method (either by wrapping the stream into a `BufferedStream` or using a `PushbackInputStream`.

```suggestion
```
created_at: 2025-01-22T00:06:48Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal;
+
+import java.util.*;
+
+/**
+ * A registry for managing the instance of an {@link SVGRasterizer}
+ * implementation. This allows for the registration and retrieval of a single
+ * rasterizer instance.
+ *
+ * @since 3.129
+ */
+public class SVGRasterizerRegistry {
+
+	/**
+	 * The instance of the registered {@link SVGRasterizer}.
+	 */
+	private static final SVGRasterizer RASTERIZER = ServiceLoader.load(SVGRasterizer.class).findFirst().orElse(null);
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/SVGRasterizerRegistry.java
user: login: HannesWell
body: Since this class now basically just holds the reference to the `SVGRasterizer` and it's getter is only called once, I suggest to move this into the `FileFormat` class and delete this class.
created_at: 2025-01-22T00:07:45Z
author_association: MEMBER
diff_hunk: @@ -155,6 +155,39 @@ public ImageData[] load(InputStream stream) {
 	return data;
 }
 
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: HannesWell
body: If I'm not mistaken your latest update removed these methods for the Linux and Mac implementation.
I assume that was unintentional, wasn't it?
created_at: 2025-01-22T00:09:07Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,173 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.*;
+
+import java.awt.*;
+import java.awt.image.*;
+import java.io.*;
+import java.util.*;
+
+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.SVGRasterizer;
+import com.github.weisj.jsvg.*;
+import com.github.weisj.jsvg.geometry.size.*;
+import com.github.weisj.jsvg.parser.*;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class implements the {@code ISVGRasterizer} interface.
+ * 
+ * @since 1.0.0
+ */
+public class JSVGRasterizer implements SVGRasterizer {
+
+	private SVGLoader svgLoader;
+
+	private final static Map<Key, Object> RENDERING_HINTS = Map.of(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON, //
+			KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY, //
+			KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY, //
+			KEY_DITHERING, VALUE_DITHER_DISABLE, //
+			KEY_FRACTIONALMETRICS, VALUE_FRACTIONALMETRICS_ON, //
+			KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC, //
+			KEY_RENDERING, VALUE_RENDER_QUALITY, //
+			KEY_STROKE_CONTROL, VALUE_STROKE_PURE, //
+			KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON //
+	);
+
+	@Override
+	public ImageData[] rasterizeSVG(InputStream stream, int zoom) throws IOException {
+		if (stream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		if (!stream.markSupported()) {
+			stream = new BufferedInputStream(stream);
+		}
+		if (isSVGFile(stream)) {
+			stream.mark(Integer.MAX_VALUE);
+			if (svgLoader == null) {
+				svgLoader = new SVGLoader();
+			}
+			SVGDocument svgDocument = null;
+			InputStream nonClosingStream = new FilterInputStream(stream) {
+				@Override
+				public void close() throws IOException {
+					// Do nothing to prevent closing the underlying stream
+				}
+			};
+			svgDocument = svgLoader.load(nonClosingStream, null, LoaderContext.createDefault());
+			stream.reset();
+			if (svgDocument != null) {
+				float scalingFactor = zoom / 100.0f;
+				FloatSize size = svgDocument.size();
+				double originalWidth = size.getWidth();
+				double originalHeight = size.getHeight();
+				int scaledWidth = (int) Math.round(originalWidth * scalingFactor);
+				int scaledHeight = (int) Math.round(originalHeight * scalingFactor);
+				BufferedImage image = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_ARGB);
+				Graphics2D g = image.createGraphics();
+				g.setRenderingHints(RENDERING_HINTS);
+				g.scale(scalingFactor, scalingFactor);
+				svgDocument.render(null, g);
+				g.dispose();
+				return new ImageData[] { convertToSWT(image) };
+			} else {
+				SWT.error(SWT.ERROR_INVALID_IMAGE);
+			}
+		}
+		return null;
+	}
+
+	private ImageData convertToSWT(BufferedImage bufferedImage) {
+		if (bufferedImage.getColorModel() instanceof DirectColorModel) {
+			DirectColorModel colorModel = (DirectColorModel) bufferedImage.getColorModel();
+			PaletteData palette = new PaletteData(colorModel.getRedMask(), colorModel.getGreenMask(),
+					colorModel.getBlueMask());
+			ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(),
+					colorModel.getPixelSize(), palette);
+			for (int y = 0; y < data.height; y++) {
+				for (int x = 0; x < data.width; x++) {
+					int rgb = bufferedImage.getRGB(x, y);
+					int pixel = palette.getPixel(new RGB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF));
+					data.setPixel(x, y, pixel);
+					if (colorModel.hasAlpha()) {
+						data.setAlpha(x, y, (rgb >> 24) & 0xFF);
+					}
+				}
+			}
+			return data;
+		} else if (bufferedImage.getColorModel() instanceof IndexColorModel) {
+			IndexColorModel colorModel = (IndexColorModel) bufferedImage.getColorModel();
+			int size = colorModel.getMapSize();
+			byte[] reds = new byte[size];
+			byte[] greens = new byte[size];
+			byte[] blues = new byte[size];
+			colorModel.getReds(reds);
+			colorModel.getGreens(greens);
+			colorModel.getBlues(blues);
+			RGB[] rgbs = new RGB[size];
+			for (int i = 0; i < rgbs.length; i++) {
+				rgbs[i] = new RGB(reds[i] & 0xFF, greens[i] & 0xFF, blues[i] & 0xFF);
+			}
+			PaletteData palette = new PaletteData(rgbs);
+			ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(),
+					colorModel.getPixelSize(), palette);
+			data.transparentPixel = colorModel.getTransparentPixel();
+			WritableRaster raster = bufferedImage.getRaster();
+			int[] pixelArray = new int[1];
+			for (int y = 0; y < data.height; y++) {
+				for (int x = 0; x < data.width; x++) {
+					raster.getPixel(x, y, pixelArray);
+					data.setPixel(x, y, pixelArray[0]);
+				}
+			}
+			return data;
+		} else if (bufferedImage.getColorModel() instanceof ComponentColorModel) {
+			ComponentColorModel colorModel = (ComponentColorModel) bufferedImage.getColorModel();
+			// ASSUMES: 3 BYTE BGR IMAGE TYPE
+			PaletteData palette = new PaletteData(0x0000FF, 0x00FF00, 0xFF0000);
+			ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(),
+					colorModel.getPixelSize(), palette);
+			// This is valid because we are using a 3-byte Data model with no transparent
+			// pixels
+			data.transparentPixel = -1;
+			WritableRaster raster = bufferedImage.getRaster();
+			int[] pixelArray = new int[3];
+			for (int y = 0; y < data.height; y++) {
+				for (int x = 0; x < data.width; x++) {
+					raster.getPixel(x, y, pixelArray);
+					int pixel = palette.getPixel(new RGB(pixelArray[0], pixelArray[1], pixelArray[2]));
+					data.setPixel(x, y, pixel);
+				}
+			}
+			return data;
+		}
+		return null;
+	}
+
+	private boolean isSVGFile(InputStream stream) throws IOException {
+		if (stream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		stream.mark(Integer.MAX_VALUE);
+		try {
+			int firstByte = stream.read();
+			return firstByte == '<';
+		} finally {
+			stream.reset();
+		}
+	}
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
user: login: HannesWell
body: If this would be moved into the `FileFormat` we can do all special stream handling and going back and forth in the stream (if necessary) there. And this class can then just read from the stream assuming it's content is an SVG and doesn't has to care about marking, resetting etc.

And having a check if the content of an InputStream is an SVG file in a FileFormat class seems to be suitable too. And it avoids to re-implement or move this method in case there are alternative implementations at any point in time.
created_at: 2025-01-22T00:13:54Z
author_association: MEMBER
diff_hunk: @@ -155,6 +155,39 @@ public ImageData[] load(InputStream stream) {
 	return data;
 }
 
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: Michael5601
body: It was intentional as these implementations were only half finished and the implementations for the `Image` and `ImageData` classes were missing completely. As soon as windows is finished I will provide a full implementation for the other OS which ist not much work then. 
created_at: 2025-01-22T10:31:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal;
+
+import java.util.*;
+
+/**
+ * A registry for managing the instance of an {@link SVGRasterizer}
+ * implementation. This allows for the registration and retrieval of a single
+ * rasterizer instance.
+ *
+ * @since 3.129
+ */
+public class SVGRasterizerRegistry {
+
+	/**
+	 * The instance of the registered {@link SVGRasterizer}.
+	 */
+	private static final SVGRasterizer RASTERIZER = ServiceLoader.load(SVGRasterizer.class).findFirst().orElse(null);
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/SVGRasterizerRegistry.java
user: login: Michael5601
body: done.
created_at: 2025-01-22T10:35:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,173 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.*;
+
+import java.awt.*;
+import java.awt.image.*;
+import java.io.*;
+import java.util.*;
+
+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.SVGRasterizer;
+import com.github.weisj.jsvg.*;
+import com.github.weisj.jsvg.geometry.size.*;
+import com.github.weisj.jsvg.parser.*;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class implements the {@code ISVGRasterizer} interface.
+ * 
+ * @since 1.0.0
+ */
+public class JSVGRasterizer implements SVGRasterizer {
+
+	private SVGLoader svgLoader;
+
+	private final static Map<Key, Object> RENDERING_HINTS = Map.of(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON, //
+			KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY, //
+			KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY, //
+			KEY_DITHERING, VALUE_DITHER_DISABLE, //
+			KEY_FRACTIONALMETRICS, VALUE_FRACTIONALMETRICS_ON, //
+			KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC, //
+			KEY_RENDERING, VALUE_RENDER_QUALITY, //
+			KEY_STROKE_CONTROL, VALUE_STROKE_PURE, //
+			KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON //
+	);
+
+	@Override
+	public ImageData[] rasterizeSVG(InputStream stream, int zoom) throws IOException {
+		if (stream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		if (!stream.markSupported()) {
+			stream = new BufferedInputStream(stream);
+		}
+		if (isSVGFile(stream)) {
+			stream.mark(Integer.MAX_VALUE);
+			if (svgLoader == null) {
+				svgLoader = new SVGLoader();
+			}
+			SVGDocument svgDocument = null;
+			InputStream nonClosingStream = new FilterInputStream(stream) {
+				@Override
+				public void close() throws IOException {
+					// Do nothing to prevent closing the underlying stream
+				}
+			};
+			svgDocument = svgLoader.load(nonClosingStream, null, LoaderContext.createDefault());
+			stream.reset();
+			if (svgDocument != null) {
+				float scalingFactor = zoom / 100.0f;
+				FloatSize size = svgDocument.size();
+				double originalWidth = size.getWidth();
+				double originalHeight = size.getHeight();
+				int scaledWidth = (int) Math.round(originalWidth * scalingFactor);
+				int scaledHeight = (int) Math.round(originalHeight * scalingFactor);
+				BufferedImage image = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_ARGB);
+				Graphics2D g = image.createGraphics();
+				g.setRenderingHints(RENDERING_HINTS);
+				g.scale(scalingFactor, scalingFactor);
+				svgDocument.render(null, g);
+				g.dispose();
+				return new ImageData[] { convertToSWT(image) };
+			} else {
+				SWT.error(SWT.ERROR_INVALID_IMAGE);
+			}
+		}
+		return null;
+	}
+
+	private ImageData convertToSWT(BufferedImage bufferedImage) {
+		if (bufferedImage.getColorModel() instanceof DirectColorModel) {
+			DirectColorModel colorModel = (DirectColorModel) bufferedImage.getColorModel();
+			PaletteData palette = new PaletteData(colorModel.getRedMask(), colorModel.getGreenMask(),
+					colorModel.getBlueMask());
+			ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(),
+					colorModel.getPixelSize(), palette);
+			for (int y = 0; y < data.height; y++) {
+				for (int x = 0; x < data.width; x++) {
+					int rgb = bufferedImage.getRGB(x, y);
+					int pixel = palette.getPixel(new RGB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF));
+					data.setPixel(x, y, pixel);
+					if (colorModel.hasAlpha()) {
+						data.setAlpha(x, y, (rgb >> 24) & 0xFF);
+					}
+				}
+			}
+			return data;
+		} else if (bufferedImage.getColorModel() instanceof IndexColorModel) {
+			IndexColorModel colorModel = (IndexColorModel) bufferedImage.getColorModel();
+			int size = colorModel.getMapSize();
+			byte[] reds = new byte[size];
+			byte[] greens = new byte[size];
+			byte[] blues = new byte[size];
+			colorModel.getReds(reds);
+			colorModel.getGreens(greens);
+			colorModel.getBlues(blues);
+			RGB[] rgbs = new RGB[size];
+			for (int i = 0; i < rgbs.length; i++) {
+				rgbs[i] = new RGB(reds[i] & 0xFF, greens[i] & 0xFF, blues[i] & 0xFF);
+			}
+			PaletteData palette = new PaletteData(rgbs);
+			ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(),
+					colorModel.getPixelSize(), palette);
+			data.transparentPixel = colorModel.getTransparentPixel();
+			WritableRaster raster = bufferedImage.getRaster();
+			int[] pixelArray = new int[1];
+			for (int y = 0; y < data.height; y++) {
+				for (int x = 0; x < data.width; x++) {
+					raster.getPixel(x, y, pixelArray);
+					data.setPixel(x, y, pixelArray[0]);
+				}
+			}
+			return data;
+		} else if (bufferedImage.getColorModel() instanceof ComponentColorModel) {
+			ComponentColorModel colorModel = (ComponentColorModel) bufferedImage.getColorModel();
+			// ASSUMES: 3 BYTE BGR IMAGE TYPE
+			PaletteData palette = new PaletteData(0x0000FF, 0x00FF00, 0xFF0000);
+			ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(),
+					colorModel.getPixelSize(), palette);
+			// This is valid because we are using a 3-byte Data model with no transparent
+			// pixels
+			data.transparentPixel = -1;
+			WritableRaster raster = bufferedImage.getRaster();
+			int[] pixelArray = new int[3];
+			for (int y = 0; y < data.height; y++) {
+				for (int x = 0; x < data.width; x++) {
+					raster.getPixel(x, y, pixelArray);
+					int pixel = palette.getPixel(new RGB(pixelArray[0], pixelArray[1], pixelArray[2]));
+					data.setPixel(x, y, pixel);
+				}
+			}
+			return data;
+		}
+		return null;
+	}
+
+	private boolean isSVGFile(InputStream stream) throws IOException {
+		if (stream == null) {
+			throw new IllegalArgumentException("InputStream cannot be null");
+		}
+		stream.mark(Integer.MAX_VALUE);
+		try {
+			int firstByte = stream.read();
+			return firstByte == '<';
+		} finally {
+			stream.reset();
+		}
+	}
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
user: login: Michael5601
body: done.
created_at: 2025-01-22T10:36:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -155,6 +155,39 @@ public ImageData[] load(InputStream stream) {
 	return data;
 }
 
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData[] load(InputStream stream, int zoom) {
+	if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+    reset();
+    data = FileFormat.load(stream, zoom, this);
+    if (data == null) {
+    	load(stream);
+    }
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
user: login: Michael5601
body: done. I would be in favour to remove the marking and resetting before the rasterization because the stream is exhausted nevertheless as you mentioned in this [comment](https://github.com/eclipse-platform/eclipse.platform.swt/pull/1638#discussion_r1921560548). 

I remembered the only reason I did this in the first place was a comment in this discussion that it would be pointless to even use a stream if we exhaust it in the method. But I might have gotten it wrong back then. I removed it for now.
created_at: 2025-01-22T10:46:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.*;
+
+import java.awt.*;
+import java.awt.image.*;
+import java.io.*;
+import java.util.*;
+import org.eclipse.swt.graphics.SVGRasterizer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.graphics.RGB;
+import com.github.weisj.jsvg.*;
+import com.github.weisj.jsvg.geometry.size.*;
+import com.github.weisj.jsvg.parser.*;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class implements the {@code ISVGRasterizer} interface.
+ * 
+ * @since 1.0.0
+ */
+public class JSVGRasterizer implements SVGRasterizer {
+	
+	private SVGLoader svgLoader;
+
+	private final static Map<Key, Object> RENDERING_HINTS = Map.of(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON, //
+			KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY, //
+			KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY, //
+			KEY_DITHERING, VALUE_DITHER_DISABLE, //
+			KEY_FRACTIONALMETRICS, VALUE_FRACTIONALMETRICS_ON, //
+			KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC, //
+			KEY_RENDERING, VALUE_RENDER_QUALITY, //
+			KEY_STROKE_CONTROL, VALUE_STROKE_PURE, //
+			KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON //
+	);
+
+	@Override
+	public ImageData rasterizeSVG(InputStream stream, float scalingFactor) throws IOException {
+		if (stream == null) {
+	        throw new IllegalArgumentException("InputStream cannot be null");
+	    }
+		stream.mark(Integer.MAX_VALUE);
+		if(svgLoader == null) {
+			svgLoader = new SVGLoader();
+		}
+		SVGDocument svgDocument = null;
+		InputStream nonClosingStream = new FilterInputStream(stream) {
+		        @Override
+		        public void close() throws IOException {
+		            // Do nothing to prevent closing the underlying stream
+		        }
+		    };
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
user: login: Michael5601
body: The workaround is necessary with version 1.6.1 of jsvg. I did not yet try with newer versions.
I removed the marking and resetting for the rasterization. 
created_at: 2025-01-22T10:59:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -331,7 +331,39 @@ scanlinePad, checkData(data), 0, null,
  * @see ImageLoader#load(InputStream)
  */
 public ImageData(InputStream stream) {
-	ImageData[] data = ImageDataLoader.load(stream);
+	this(stream, 0);
+}
+
+/**
+ * Constructs an <code>ImageData</code> loaded from the specified
+ * input stream. Throws an error if an error occurs while loading
+ * the image, or if the image has an unsupported type.  Application
+ * code is still responsible for closing the input stream.
+ * <p>
+ * This constructor is provided for convenience when loading a single
+ * image only. If the stream contains multiple images, only the first
+ * one will be loaded. To load multiple images, use
+ * <code>ImageLoader.load()</code>.
+ * </p>
+ *
+ * @param stream the input stream to load the image from (must not be null)
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageData.java
user: login: Michael5601
body: Checking all javadocs is on my ToDo list :D was kinda pointless by the many changes that were happening in the code. 
created_at: 2025-01-22T11:01:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -400,6 +456,59 @@ public ImageData(String filename) {
 		i.delayTime);
 }
 
+/**
+ * Constructs an <code>ImageData</code> loaded from a file with the
+ * specified name. Throws an error if an error occurs loading the
+ * image, or if the image has an unsupported type.
+ *
+ * @param filename the name of the file to load the image from (must not be null)
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * This case is equivalent to calling {@link ImageLoader#load(String)}.
+ *
+ * A value above 0 specifies a scaling factor for the output image. For example:
+ * <ul>
+ *   <li>A value of 100 maintains the original size of the SVG when rasterized.</li>
+ *   <li>A value of 200 doubles the size of the rasterized image.</li>
+ *   <li>A value of 50 reduces the size of the rasterized image to half.</li>
+ * </ul>
+ * The scaling is applied uniformly to both width and height.
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the file</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
+ * </ul>
+ *
+ * @since 3.129
+ */
+public ImageData(String filename, int zoom) {
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageData.java
user: login: HannesWell
body: In general using String to model file-system paths is a bad idea. But that's how it's done in the existing API.
I have created a PR with the proposal to replace it:
- https://github.com/eclipse-platform/eclipse.platform.swt/pull/1767

If that is successful I think we should use `File` directly in new methods.
created_at: 2025-01-26T22:04:01Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: SWT SVG Rendering Support
+Bundle-SymbolicName: org.eclipse.swt.svg
+Bundle-Version: 1.0.0.qualifier
+Automatic-Module-Name: org.eclipse.swt.svgPlugin
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Fragment-Host: org.eclipse.swt
+Import-Package: com.github.weisj.jsvg;version="[1.7.0,2.0.0)",
+ com.github.weisj.jsvg.geometry.size;version="[1.7.0,2.0.0)",
+ com.github.weisj.jsvg.parser;version="[1.7.0,2.0.0)",
+ org.eclipse.swt.internal.image
+Export-Package: org.eclipse.swt.svg
+Require-Capability: eclipse.swt.native;filter:="(eclipse.swt.native=native)"
path: bundles/org.eclipse.swt.svg/META-INF/MANIFEST.MF
user: login: HannesWell
body: Here we have two options on regarding how to wire (pull in) a current native fragment on the (compile) classpath of this bundle:

- Require a general capability (e.g. `eclipse.swt.native`) that is provided by each fragment.
This could later also be used to introduce a requirement from the SWT host bundle to one of it's fragments, but that requires more work.
- Import an internal package that is only exported by the native fragments, but not by the `o.e.swt` host.
This requires to remove the export of that internal package from the `o.e.swt` host bundle. Currently I don't see a practical disadvantage from the latter.

Personally I have no favorite yet.
created_at: 2025-02-23T21:32:38Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: SWT SVG Rendering Support
+Bundle-SymbolicName: org.eclipse.swt.svg
+Bundle-Version: 1.0.0.qualifier
+Automatic-Module-Name: org.eclipse.swt.svgPlugin
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Fragment-Host: org.eclipse.swt
+Import-Package: com.github.weisj.jsvg;version="[1.7.0,2.0.0)",
+ com.github.weisj.jsvg.geometry.size;version="[1.7.0,2.0.0)",
+ com.github.weisj.jsvg.parser;version="[1.7.0,2.0.0)",
+ org.eclipse.swt.internal.image
+Export-Package: org.eclipse.swt.svg
+Require-Capability: eclipse.swt.native;filter:="(eclipse.swt.native=native)"
path: bundles/org.eclipse.swt.svg/META-INF/MANIFEST.MF
user: login: laeubi
body: Both things are wrong. All fragments already have a requirement to their host and all fragments Req/Cap are merged with the host at runtime. So you will end up with a host requiring `X` and providing `X` at the same time.

So if you want that something is *installed* together, then currently a `p2.inf` is the right approach, see for example here 

- https://github.com/eclipse-jdt/eclipse.jdt.debug/blob/master/org.eclipse.jdt.launching/META-INF/p2.inf
- https://github.com/eclipse-jdt/eclipse.jdt.debug/blob/master/org.eclipse.jdt.launching.ui.macosx/META-INF/p2.inf

if you only want to ensure compilation "see" the classes, you can use `jar.extra.classpath` instead.
created_at: 2025-02-24T05:32:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: SWT SVG Rendering Support
+Bundle-SymbolicName: org.eclipse.swt.svg
+Bundle-Version: 1.0.0.qualifier
+Automatic-Module-Name: org.eclipse.swt.svgPlugin
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Fragment-Host: org.eclipse.swt
+Import-Package: com.github.weisj.jsvg;version="[1.7.0,2.0.0)",
+ com.github.weisj.jsvg.geometry.size;version="[1.7.0,2.0.0)",
+ com.github.weisj.jsvg.parser;version="[1.7.0,2.0.0)",
+ org.eclipse.swt.internal.image
+Export-Package: org.eclipse.swt.svg
+Require-Capability: eclipse.swt.native;filter:="(eclipse.swt.native=native)"
path: bundles/org.eclipse.swt.svg/META-INF/MANIFEST.MF
user: login: HannesWell
body: They are not wrong. From an OSGi wiring point of view they are not necessary, but that doesn't make them wrong.

> So you will end up with a host requiring X and providing X at the same time.

Exactly. But that's not wrong either. The OSGi spec or at least some OSGi blog-posts and tools like BND advocate a similar case: Importing the packages a bundle exports. If that's really always suitable is another topic, but my point is that in general this is valid.

> So if you want that something is _installed_ together, then currently a `p2.inf` is the right approach, see for example here
> if you only want to ensure compilation "see" the classes, you can use `jar.extra.classpath` instead.

Sure, but both doesn't work in this case as I have explain in https://github.com/eclipse-tycho/tycho/pull/4743#issuecomment-2677741703.
Therefore we need an enhancement in Tycho to control the compilation order and compile-classpath for fragments of the same host, where one depends on the other in more detail.


created_at: 2025-02-24T08:48:51Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: SWT SVG Rendering Support
+Bundle-SymbolicName: org.eclipse.swt.svg
+Bundle-Version: 1.0.0.qualifier
+Automatic-Module-Name: org.eclipse.swt.svgPlugin
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Fragment-Host: org.eclipse.swt
+Import-Package: com.github.weisj.jsvg;version="[1.7.0,2.0.0)",
+ com.github.weisj.jsvg.geometry.size;version="[1.7.0,2.0.0)",
+ com.github.weisj.jsvg.parser;version="[1.7.0,2.0.0)",
+ org.eclipse.swt.internal.image
+Export-Package: org.eclipse.swt.svg
+Require-Capability: eclipse.swt.native;filter:="(eclipse.swt.native=native)"
path: bundles/org.eclipse.swt.svg/META-INF/MANIFEST.MF
user: login: laeubi
body: >  The OSGi spec or at least some OSGi blog-posts and tools like BND advocate a similar case: Importing the packages a bundle exports.

Import package has special treatments in the spec that do not apply to generic requirements. And I'm not trying to count the many times even this has created big headache, so I really like to avoid such hacks to fix build-problems.

> Therefore we need an enhancement in Tycho to control the compilation order and compile-classpath for fragments of the same host, where one depends on the other in more detail.

Tycho already supports this in several ways. Adding *runtime data* to fix *compile time* problem looks really wrong.

created_at: 2025-02-24T08:56:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: SWT SVG Rendering Support
+Bundle-SymbolicName: org.eclipse.swt.svg
+Bundle-Version: 1.0.0.qualifier
+Automatic-Module-Name: org.eclipse.swt.svgPlugin
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Fragment-Host: org.eclipse.swt
+Import-Package: com.github.weisj.jsvg;version="[1.7.0,2.0.0)",
+ com.github.weisj.jsvg.geometry.size;version="[1.7.0,2.0.0)",
+ com.github.weisj.jsvg.parser;version="[1.7.0,2.0.0)",
+ org.eclipse.swt.internal.image
+Export-Package: org.eclipse.swt.svg
+Require-Capability: eclipse.swt.native;filter:="(eclipse.swt.native=native)"
path: bundles/org.eclipse.swt.svg/META-INF/MANIFEST.MF
user: login: Michael5601
body: Thank you @HannesWell :) 
> I have pushed two more commits that both should be incorporated into yours eventually.

Are you referring to the Tycho Draft or what is there that I can already incorporate? Your changes work fine for me. 

> Regarding the tests:
First the new test-bundle should be moved into the tests folder and second, I'm not sure if we should really introduce a new test bundle just for that case. This seems to be a great overhead. On the other hand this allows to test SWT with the SVG support being absent.

I moved the test-bundle into the `tests`  folder. Originally I created the seperate test bundle so that SWT is completely seperated from the SVG feature.
created_at: 2025-02-24T12:24:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: SWT SVG Rendering Support
+Bundle-SymbolicName: org.eclipse.swt.svg
+Bundle-Version: 1.0.0.qualifier
+Automatic-Module-Name: org.eclipse.swt.svgPlugin
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Fragment-Host: org.eclipse.swt
+Import-Package: com.github.weisj.jsvg;version="[1.7.0,2.0.0)",
+ com.github.weisj.jsvg.geometry.size;version="[1.7.0,2.0.0)",
+ com.github.weisj.jsvg.parser;version="[1.7.0,2.0.0)",
+ org.eclipse.swt.internal.image
+Export-Package: org.eclipse.swt.svg
+Require-Capability: eclipse.swt.native;filter:="(eclipse.swt.native=native)"
path: bundles/org.eclipse.swt.svg/META-INF/MANIFEST.MF
user: login: HannesWell
body: I have now changed the configuration to add all SWT native fragments via `jars.extra.classpath` entries to the compile-classpath.
This still required an adjustment in Tycho, which was done via https://github.com/eclipse-tycho/tycho/pull/4771 and should be available for the Tycho 4.0.x line this evening. Then the build should finally succeed. I'll update this PR then.

Since all fragments are now added via  `jars.extra.classpath`, they can be removed from the `.classpath` file.

created_at: 2025-03-01T17:13:36Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,5 @@
+source.. = JUnit Tests/,
+output.. = bin/ 
+bin.includes = .,\
+               META-INF/
+tycho.pomless.parent = ../../local-build/local-build-parent
path: tests/org.eclipse.swt.svg.tests/build.properties
user: login: HeikoKlare
body: If we add this parent pom definition to the existing `org.eclipse.swt.tests` bundle and remove it here and from the `org.eclipse.swt.svg` bundle and add the new svg framgent to the `p2.inf` of `org.eclipse.swt.fragments.localbuild` instead, it should be possible to move the single test class from this new SVG-specific test bundle to that existing test bundle, shouldn't it? At least a local build with that change succeeds for me.
created_at: 2025-03-03T15:12:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+source.. = JUnit Tests/,
+output.. = bin/ 
+bin.includes = .,\
+               META-INF/
+tycho.pomless.parent = ../../local-build/local-build-parent
path: tests/org.eclipse.swt.svg.tests/build.properties
user: login: HannesWell
body: Yes that should work and should also ensure that the SVG fragment is also added to test-runtimes where `o.e.swt` is included in the I-build (but I'm not 100% sure and it would have to be verified).
created_at: 2025-03-03T22:40:49Z
author_association: MEMBER
diff_hunk: @@ -28,6 +28,7 @@
 
   <properties>
     <tycho.scmUrl>scm:git:https://github.com/eclipse-platform/eclipse.platform.swt.git</tycho.scmUrl>
+    <tycho.version>4.0.13-SNAPSHOT</tycho.version>
path: pom.xml
user: login: HannesWell
body: This should be removed before submitting this version also has to be used in I-builds then.
created_at: 2025-03-03T22:41:32Z
author_association: MEMBER
diff_hunk: @@ -28,6 +28,7 @@
 
   <properties>
     <tycho.scmUrl>scm:git:https://github.com/eclipse-platform/eclipse.platform.swt.git</tycho.scmUrl>
+    <tycho.version>4.0.13-SNAPSHOT</tycho.version>
path: pom.xml
user: login: HeikoKlare
body: Just saw that we are still using older pomless extensions throughout the projects (such as 4.0.8 in platform and 4.0.10 here):
https://github.com/eclipse-platform/eclipse.platform.swt/blob/cac15c334b8d2cfe71bd2fd55e0a53935af32b6b/.mvn/extensions.xml#L7

Probably not an actual issue though.
created_at: 2025-03-04T16:52:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -84,6 +87,16 @@ List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom)
 		}
 	}
 
+	static abstract class DynamicImageFileFormat extends FileFormat {
+
+		abstract ImageData[] loadFromByteStream(int targetZoom);
+
+		@Override
+		List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom) {
+			return Arrays.stream(loadFromByteStream(targetZoom)).map(d -> new ElementAtZoom<>(d, fileZoom)).toList();
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java
user: login: HeikoKlare
body: ```suggestion
			return Arrays.stream(loadFromByteStream(100 * targetZoom / fileZoom)).map(d -> new ElementAtZoom<>(d, targetZoom)).toList();
```
created_at: 2025-03-11T19:19:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGFileFormat.java
user: login: HeikoKlare
body: ```suggestion
 * Contributors:
 *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
```
created_at: 2025-03-11T19:22:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: SWT SVG Rendering Support
+Bundle-SymbolicName: org.eclipse.swt.svg
+Bundle-Version: 3.130.0.qualifier
+Automatic-Module-Name: org.eclipse.swt.svgPlugin
path: bundles/org.eclipse.swt.svg/META-INF/MANIFEST.MF
user: login: HeikoKlare
body: ```suggestion
Automatic-Module-Name: org.eclipse.swt.svg
```
created_at: 2025-03-11T19:22:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal;
+
+import java.io.*;
+
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/SVGRasterizer.java
user: login: HeikoKlare
body: ```suggestion
 * @since 3.130
```
created_at: 2025-03-11T19:23:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/SVGRasterizer.java
user: login: HeikoKlare
body: ```suggestion
 * Contributors:
 *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
```
created_at: 2025-03-11T19:23:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal;
+
+import java.io.*;
+
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface SVGRasterizer {
+	/**
+	 * Rasterizes an SVG image from the provided {@code InputStream} using the
+	 * specified zoom factor.
+	 *
+	 * @param stream the SVG image as an {@link InputStream}.
+	 * @param zoom   the scaling factor e.g. 200 for doubled size. This value must
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/SVGRasterizer.java
user: login: HeikoKlare
body: Maybe make the value being defined in percent explicit (so that it's clear that default is 100).
```suggestion
	 * @param zoom   the scaling factor (in percent) e.g. 200 for doubled size. This value must
```
created_at: 2025-03-11T19:24:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.tests.junit;
+
+import static org.eclipse.swt.tests.junit.SwtTestUtil.assertSWTProblem;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.net.URL;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageDataProvider;
+import org.eclipse.swt.graphics.ImageFileNameProvider;
+import org.eclipse.swt.widgets.Display;
+import org.junit.Before;
+import org.junit.Test;
+
+public class Test_org_eclipse_swt_internal_SVGRasterizer {
+
+	Display display;
+
+	ImageFileNameProvider imageFileNameProvider = zoom -> {
+		String fileName = "collapseall.svg";
+		return getPath(fileName);
+	};
+
+	ImageDataProvider imageDataProvider = zoom -> {
+		String fileName = "collapseall.svg";
+		return new ImageData(getPath(fileName));
+	};
+
+	@Before
+	public void setUp() {
+		display = Display.getDefault();
path: tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_internal_SVGRasterizer.java
user: login: HeikoKlare
body: You could also inline this instead of storing it in a instance field, or at least retrieve it via a `getDisplay()` method on demand instead of storing it in a field.
created_at: 2025-03-11T19:26:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.tests.junit;
+
+import static org.eclipse.swt.tests.junit.SwtTestUtil.assertSWTProblem;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.net.URL;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageDataProvider;
+import org.eclipse.swt.graphics.ImageFileNameProvider;
+import org.eclipse.swt.widgets.Display;
+import org.junit.Before;
+import org.junit.Test;
+
+public class Test_org_eclipse_swt_internal_SVGRasterizer {
+
+	Display display;
+
+	ImageFileNameProvider imageFileNameProvider = zoom -> {
+		String fileName = "collapseall.svg";
+		return getPath(fileName);
+	};
+
+	ImageDataProvider imageDataProvider = zoom -> {
+		String fileName = "collapseall.svg";
+		return new ImageData(getPath(fileName));
+	};
+
+	@Before
+	public void setUp() {
+		display = Display.getDefault();
+	}
+
+	String getPath(String fileName) {
+		String urlPath = "";
+		String pluginPath = System.getProperty("PLUGIN_PATH");
+		if (pluginPath == null) {
+			URL url = getClass().getClassLoader().getResource(fileName);
+			if (url == null) {
+				fail("URL == null for file " + fileName);
+			}
+			urlPath = url.getFile();
+		} else {
+			urlPath = pluginPath + "/data/" + fileName;
+		}
+		if (File.separatorChar != '/')
+			urlPath = urlPath.replace('/', File.separatorChar);
+		if (SwtTestUtil.isWindows && urlPath.indexOf(File.separatorChar) == 0)
+			urlPath = urlPath.substring(1);
+		urlPath = urlPath.replaceAll("%20", " ");
+		return urlPath;
+	}
+
+	@Test
+	public void test_ConstructorLorg_eclipse_swt_graphics_Device_ImageFileNameProvider() {
+		// Valid provider
+		Image image = new Image(display, imageFileNameProvider);
path: tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_internal_SVGRasterizer.java
user: login: HeikoKlare
body: It feels a bit inconsistent to define the "proper" provider as a field and the "corrupt" one in the method. Maybe both could be defined within the method as they are not shared, so that it's easier to understand the code without jumping around?
created_at: 2025-03-11T19:27:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal;
+
+import java.io.*;
+
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface SVGRasterizer {
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/SVGRasterizer.java
user: login: HeikoKlare
body: Maybe this would be even better placed this in the package `org.eclipse.swt.internal.image`.
created_at: 2025-03-14T08:33:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -84,6 +87,16 @@ List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom)
 		}
 	}
 
+	static abstract class DynamicImageFileFormat extends FileFormat {
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java
user: login: HeikoKlare
body: Since the FileFormat itself it already rather dynamic since the latest refactoring and since there is only a single implementation of this class far (and no others are planned to be added), we could remove this intermediate class for now and only keep it for the static format, for which different implementations exist. So as a result, the `SVGFileFormat` could directly implement `FileFormat`.
created_at: 2025-03-14T08:37:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -38,3 +38,4 @@ requires.8.namespace = org.eclipse.equinox.p2.iu
 requires.8.name = org.eclipse.swt.gtk.linux.riscv64
 requires.8.range = 0.0.0
 requires.8.filter = (&(osgi.os=linux)(osgi.ws=gtk)(osgi.arch=riscv64))
+
path: local-build/org.eclipse.swt.fragments.localbuild/META-INF/p2.inf
user: login: HeikoKlare
body: Formatting change should be removed.
created_at: 2025-03-14T08:37:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal;
+
+import java.io.*;
+
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface SVGRasterizer {
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/SVGRasterizer.java
user: login: Michael5601
body: done.
created_at: 2025-03-17T08:03:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -38,3 +38,4 @@ requires.8.namespace = org.eclipse.equinox.p2.iu
 requires.8.name = org.eclipse.swt.gtk.linux.riscv64
 requires.8.range = 0.0.0
 requires.8.filter = (&(osgi.os=linux)(osgi.ws=gtk)(osgi.arch=riscv64))
+
path: local-build/org.eclipse.swt.fragments.localbuild/META-INF/p2.inf
user: login: Michael5601
body: done.
created_at: 2025-03-17T08:03:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -84,6 +87,16 @@ List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom)
 		}
 	}
 
+	static abstract class DynamicImageFileFormat extends FileFormat {
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java
user: login: Michael5601
body: done.
created_at: 2025-03-17T08:03:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal;
+
+import java.io.*;
+
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
+ */
+public interface SVGRasterizer {
+	/**
+	 * Rasterizes an SVG image from the provided {@code InputStream} using the
+	 * specified zoom factor.
+	 *
+	 * @param stream the SVG image as an {@link InputStream}.
+	 * @param zoom   the scaling factor e.g. 200 for doubled size. This value must
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/SVGRasterizer.java
user: login: Michael5601
body: done.
created_at: 2025-03-17T08:04:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/SVGRasterizer.java
user: login: Michael5601
body: done.
created_at: 2025-03-17T08:04:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal;
+
+import java.io.*;
+
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.129
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/SVGRasterizer.java
user: login: Michael5601
body: done.
created_at: 2025-03-17T08:04:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: SWT SVG Rendering Support
+Bundle-SymbolicName: org.eclipse.swt.svg
+Bundle-Version: 3.130.0.qualifier
+Automatic-Module-Name: org.eclipse.swt.svgPlugin
path: bundles/org.eclipse.swt.svg/META-INF/MANIFEST.MF
user: login: Michael5601
body: done.
created_at: 2025-03-17T08:04:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGFileFormat.java
user: login: Michael5601
body: done.
created_at: 2025-03-17T08:04:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -84,6 +87,16 @@ List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom)
 		}
 	}
 
+	static abstract class DynamicImageFileFormat extends FileFormat {
+
+		abstract ImageData[] loadFromByteStream(int targetZoom);
+
+		@Override
+		List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom) {
+			return Arrays.stream(loadFromByteStream(targetZoom)).map(d -> new ElementAtZoom<>(d, fileZoom)).toList();
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java
user: login: Michael5601
body: I don't quite understand the first part of this change (100 * targetZoom / fileZoom) and it also does not work locally for me. 
For SVGs the targetZoom and the fileZoom are always the same. 
So if the targetZoom is 125 we would get 100 * 125 = 12.500. And then we divide this by the fileZoom which is also 125: 12.500 / 125 = 100. 
The icons requested will always be 100 percent size which is wrong as we want 125 percent size. This is also the case for every other targetZoom. 

Please correct me if I am missing something.

The second part of the change has no real impact as targetZoom and fileZoom is the same but I agree with you that targetZoom is still the better choice.
created_at: 2025-03-17T08:15:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.tests.junit;
+
+import static org.eclipse.swt.tests.junit.SwtTestUtil.assertSWTProblem;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.net.URL;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageDataProvider;
+import org.eclipse.swt.graphics.ImageFileNameProvider;
+import org.eclipse.swt.widgets.Display;
+import org.junit.Before;
+import org.junit.Test;
+
+public class Test_org_eclipse_swt_internal_SVGRasterizer {
+
+	Display display;
+
+	ImageFileNameProvider imageFileNameProvider = zoom -> {
+		String fileName = "collapseall.svg";
+		return getPath(fileName);
+	};
+
+	ImageDataProvider imageDataProvider = zoom -> {
+		String fileName = "collapseall.svg";
+		return new ImageData(getPath(fileName));
+	};
+
+	@Before
+	public void setUp() {
+		display = Display.getDefault();
path: tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_internal_SVGRasterizer.java
user: login: Michael5601
body: I inlined the field. Thanks for the hint. :)
created_at: 2025-03-17T08:36:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.tests.junit;
+
+import static org.eclipse.swt.tests.junit.SwtTestUtil.assertSWTProblem;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.net.URL;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageDataProvider;
+import org.eclipse.swt.graphics.ImageFileNameProvider;
+import org.eclipse.swt.widgets.Display;
+import org.junit.Before;
+import org.junit.Test;
+
+public class Test_org_eclipse_swt_internal_SVGRasterizer {
+
+	Display display;
+
+	ImageFileNameProvider imageFileNameProvider = zoom -> {
+		String fileName = "collapseall.svg";
+		return getPath(fileName);
+	};
+
+	ImageDataProvider imageDataProvider = zoom -> {
+		String fileName = "collapseall.svg";
+		return new ImageData(getPath(fileName));
+	};
+
+	@Before
+	public void setUp() {
+		display = Display.getDefault();
+	}
+
+	String getPath(String fileName) {
+		String urlPath = "";
+		String pluginPath = System.getProperty("PLUGIN_PATH");
+		if (pluginPath == null) {
+			URL url = getClass().getClassLoader().getResource(fileName);
+			if (url == null) {
+				fail("URL == null for file " + fileName);
+			}
+			urlPath = url.getFile();
+		} else {
+			urlPath = pluginPath + "/data/" + fileName;
+		}
+		if (File.separatorChar != '/')
+			urlPath = urlPath.replace('/', File.separatorChar);
+		if (SwtTestUtil.isWindows && urlPath.indexOf(File.separatorChar) == 0)
+			urlPath = urlPath.substring(1);
+		urlPath = urlPath.replaceAll("%20", " ");
+		return urlPath;
+	}
+
+	@Test
+	public void test_ConstructorLorg_eclipse_swt_graphics_Device_ImageFileNameProvider() {
+		// Valid provider
+		Image image = new Image(display, imageFileNameProvider);
path: tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_internal_SVGRasterizer.java
user: login: Michael5601
body: All providers are defined in the method now. I changed the name of the providers to "corruptImageFileNameProvider" and "validImageFileNameProvider" so the comments can be deleted.
created_at: 2025-03-17T08:37:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -84,6 +87,16 @@ List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom)
 		}
 	}
 
+	static abstract class DynamicImageFileFormat extends FileFormat {
+
+		abstract ImageData[] loadFromByteStream(int targetZoom);
+
+		@Override
+		List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom) {
+			return Arrays.stream(loadFromByteStream(targetZoom)).map(d -> new ElementAtZoom<>(d, fileZoom)).toList();
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java
user: login: HeikoKlare
body: The issue here is that `fileZoom` will usually be 100 for SVGs (as that's what we assume for them). This is because just rendering the SVG at original scale, it will produce a 100% image. With the mechanisms in Platform UI, you could also specify, for example, a 2x version of the SVG icon, but that does not really make sense. But to still ensure that it works, the `fileZoom` has to be considered when rendering the SVG, as a 200%-scaled SVG with a target zoom of 150 needs to be rendered at 100 * targetZoom / fileZoom = 100 * 150 / 200.
created_at: 2025-03-17T11:42:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -84,6 +87,16 @@ List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom)
 		}
 	}
 
+	static abstract class DynamicImageFileFormat extends FileFormat {
+
+		abstract ImageData[] loadFromByteStream(int targetZoom);
+
+		@Override
+		List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom) {
+			return Arrays.stream(loadFromByteStream(targetZoom)).map(d -> new ElementAtZoom<>(d, fileZoom)).toList();
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/FileFormat.java
user: login: Michael5601
body: I changed it according to your suggestion. Thank you :)
created_at: 2025-03-17T11:49:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,144 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.KEY_ALPHA_INTERPOLATION;
+import static java.awt.RenderingHints.KEY_ANTIALIASING;
+import static java.awt.RenderingHints.KEY_COLOR_RENDERING;
+import static java.awt.RenderingHints.KEY_DITHERING;
+import static java.awt.RenderingHints.KEY_FRACTIONALMETRICS;
+import static java.awt.RenderingHints.KEY_INTERPOLATION;
+import static java.awt.RenderingHints.KEY_RENDERING;
+import static java.awt.RenderingHints.KEY_STROKE_CONTROL;
+import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING;
+import static java.awt.RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY;
+import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;
+import static java.awt.RenderingHints.VALUE_COLOR_RENDER_QUALITY;
+import static java.awt.RenderingHints.VALUE_DITHER_DISABLE;
+import static java.awt.RenderingHints.VALUE_FRACTIONALMETRICS_ON;
+import static java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC;
+import static java.awt.RenderingHints.VALUE_RENDER_QUALITY;
+import static java.awt.RenderingHints.VALUE_STROKE_PURE;
+import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints.Key;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.internal.image.SVGRasterizer;
+
+import com.github.weisj.jsvg.SVGDocument;
+import com.github.weisj.jsvg.geometry.size.FloatSize;
+import com.github.weisj.jsvg.parser.LoaderContext;
+import com.github.weisj.jsvg.parser.SVGLoader;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class uses the third party library JSVG for the raterization of SVG
+ * images.
+ * 
+ * @since 1.0.0
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
user: login: HeikoKlare
body: I think we do not need a since tag here as it is no part of public API.
created_at: 2025-03-17T14:21:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal.image;
+
+import java.io.*;
+import java.nio.charset.*;
+import java.util.*;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.DPIUtil.*;
+
+/**
+ * A {@link FileFormat} implementation for handling SVG (Scalable Vector Graphics) files.
+ * <p>
+ * This class detects SVG files based on their header and uses a registered
+ * {@link SVGRasterizer} service to rasterize SVG content.
+ * </p>
+ */
+public class SVGFileFormat extends FileFormat {
+
+	/** The instance of the registered {@link SVGRasterizer}. */
+	private static final SVGRasterizer RASTERIZER = ServiceLoader.load(SVGRasterizer.class).findFirst().orElse(null);
+
+	@Override
+	boolean isFileFormat(LEDataInputStream stream) throws IOException {
+		byte[] firstBytes = new byte[5];
+		int bytesRead = stream.read(firstBytes);
+		stream.unread(firstBytes);
+		String header = new String(firstBytes, 0, bytesRead, StandardCharsets.UTF_8).trim();
+		return header.startsWith("<?xml") || header.startsWith("<svg");
+	}
+
+	@Override
+	List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom) {
+		return Arrays.stream(loadFromByteStream(100 * targetZoom / fileZoom)).map(d -> new ElementAtZoom<>(d, targetZoom)).toList();
+	}
+
+	private ImageData[] loadFromByteStream(int targetZoom) {
+		try {
+			if (RASTERIZER != null && targetZoom != 0) {
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGFileFormat.java
user: login: HeikoKlare
body: Should we throw a proper error when `RASTERIZER` is null (e.g., `SWT.ERROR_UNSUPPORTED_FORMAT`) or when `targetZoom` is zero or even negative (e.g., `SWT.ERROR_INVALID_ARGUMENT`). Otherwise null will be returned and the conversion to a stream will fail, producing an incomprehensible error.
created_at: 2025-03-17T16:47:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,144 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.KEY_ALPHA_INTERPOLATION;
+import static java.awt.RenderingHints.KEY_ANTIALIASING;
+import static java.awt.RenderingHints.KEY_COLOR_RENDERING;
+import static java.awt.RenderingHints.KEY_DITHERING;
+import static java.awt.RenderingHints.KEY_FRACTIONALMETRICS;
+import static java.awt.RenderingHints.KEY_INTERPOLATION;
+import static java.awt.RenderingHints.KEY_RENDERING;
+import static java.awt.RenderingHints.KEY_STROKE_CONTROL;
+import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING;
+import static java.awt.RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY;
+import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;
+import static java.awt.RenderingHints.VALUE_COLOR_RENDER_QUALITY;
+import static java.awt.RenderingHints.VALUE_DITHER_DISABLE;
+import static java.awt.RenderingHints.VALUE_FRACTIONALMETRICS_ON;
+import static java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC;
+import static java.awt.RenderingHints.VALUE_RENDER_QUALITY;
+import static java.awt.RenderingHints.VALUE_STROKE_PURE;
+import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints.Key;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.internal.image.SVGRasterizer;
+
+import com.github.weisj.jsvg.SVGDocument;
+import com.github.weisj.jsvg.geometry.size.FloatSize;
+import com.github.weisj.jsvg.parser.LoaderContext;
+import com.github.weisj.jsvg.parser.SVGLoader;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class uses the third party library JSVG for the raterization of SVG
+ * images.
+ * 
+ * @since 1.0.0
+ */
+public class JSVGRasterizer implements SVGRasterizer {
+
+	private static final SVGLoader SVG_LOADER = new SVGLoader();
+
+	private final static Map<Key, Object> RENDERING_HINTS = Map.of( //
+			KEY_ANTIALIASING, VALUE_ANTIALIAS_ON, //
+			KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY, //
+			KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY, //
+			KEY_DITHERING, VALUE_DITHER_DISABLE, //
+			KEY_FRACTIONALMETRICS, VALUE_FRACTIONALMETRICS_ON, //
+			KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC, //
+			KEY_RENDERING, VALUE_RENDER_QUALITY, //
+			KEY_STROKE_CONTROL, VALUE_STROKE_PURE, //
+			KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON //
+	);
+
+	@Override
+	public ImageData[] rasterizeSVG(InputStream inputStream, int zoom) throws IOException {
+		SVGDocument svgDocument = loadSVG(inputStream);
+		if (svgDocument != null) {
+			return generateRasterizedImageData(svgDocument, zoom);
+		} else {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		}
+		return null;
+	}
+
+	private SVGDocument loadSVG(InputStream inputStream) {
+		return SVG_LOADER.load(inputStream, null, LoaderContext.createDefault());
+	}
+
+	private ImageData[] generateRasterizedImageData(SVGDocument svgDocument, int zoom) {
+		BufferedImage rasterizedImage = renderSVG(svgDocument, zoom);
+		return convertToSWTImageData(rasterizedImage);
+	}
+
+	private BufferedImage renderSVG(SVGDocument svgDocument, int zoom) {
+		float scalingFactor = zoom / 100.0f;
+		BufferedImage image = createImageBase(svgDocument, scalingFactor);
+		Graphics2D g = configureRenderingOptions(scalingFactor, image);
+		svgDocument.render(null, g);
+		g.dispose();
+		return image;
+	}
+
+	private BufferedImage createImageBase(SVGDocument svgDocument, float scalingFactor) {
+		FloatSize sourceImageSize = svgDocument.size();
+		int targetImageWidth = calculateTargetWidth(scalingFactor, sourceImageSize);
+		int targetImageHeight = calculateTargetHeight(scalingFactor, sourceImageSize);
+		return new BufferedImage(targetImageWidth, targetImageHeight, BufferedImage.TYPE_INT_ARGB);
+	}
+
+	private int calculateTargetWidth(float scalingFactor, FloatSize sourceImageSize) {
+		double sourceImageWidth = sourceImageSize.getWidth();
+		return (int) Math.round(sourceImageWidth * scalingFactor);
+	}
+
+	private int calculateTargetHeight(float scalingFactor, FloatSize sourceImageSize) {
+		double sourceImageHeight = sourceImageSize.getHeight();
+		return (int) Math.round(sourceImageHeight * scalingFactor);
+	}
+
+	private Graphics2D configureRenderingOptions(float scalingFactor, BufferedImage image) {
+		Graphics2D g = image.createGraphics();
+		g.setRenderingHints(RENDERING_HINTS);
+		g.scale(scalingFactor, scalingFactor);
+		return g;
+	}
+	
+	private ImageData[] convertToSWTImageData(BufferedImage rasterizedImage) {
+		int width = rasterizedImage.getWidth();
+	    int height = rasterizedImage.getHeight();
+		int[] pixels = ((DataBufferInt) rasterizedImage.getRaster().getDataBuffer()).getData();
+		PaletteData paletteData = new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF);
+        ImageData imageData = new ImageData(width, height, 32, paletteData);
+        int index = 0;
+        for (int y = 0; y < imageData.height; y++) {
+			for (int x = 0; x < imageData.width; x++) {
+				int alpha = (pixels[index] >> 24) & 0xFF;
+				imageData.setAlpha(x, y, alpha);
+				imageData.setPixel(x, y, pixels[index++]);
+			}
+		}
+        return new ImageData[]{imageData};
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
user: login: HeikoKlare
body: Some formatting is broken in this class. Please apply auto-formatting.
created_at: 2025-03-17T16:48:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,144 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.KEY_ALPHA_INTERPOLATION;
+import static java.awt.RenderingHints.KEY_ANTIALIASING;
+import static java.awt.RenderingHints.KEY_COLOR_RENDERING;
+import static java.awt.RenderingHints.KEY_DITHERING;
+import static java.awt.RenderingHints.KEY_FRACTIONALMETRICS;
+import static java.awt.RenderingHints.KEY_INTERPOLATION;
+import static java.awt.RenderingHints.KEY_RENDERING;
+import static java.awt.RenderingHints.KEY_STROKE_CONTROL;
+import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING;
+import static java.awt.RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY;
+import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;
+import static java.awt.RenderingHints.VALUE_COLOR_RENDER_QUALITY;
+import static java.awt.RenderingHints.VALUE_DITHER_DISABLE;
+import static java.awt.RenderingHints.VALUE_FRACTIONALMETRICS_ON;
+import static java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC;
+import static java.awt.RenderingHints.VALUE_RENDER_QUALITY;
+import static java.awt.RenderingHints.VALUE_STROKE_PURE;
+import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints.Key;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.internal.image.SVGRasterizer;
+
+import com.github.weisj.jsvg.SVGDocument;
+import com.github.weisj.jsvg.geometry.size.FloatSize;
+import com.github.weisj.jsvg.parser.LoaderContext;
+import com.github.weisj.jsvg.parser.SVGLoader;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class uses the third party library JSVG for the raterization of SVG
+ * images.
+ * 
+ * @since 1.0.0
+ */
+public class JSVGRasterizer implements SVGRasterizer {
+
+	private static final SVGLoader SVG_LOADER = new SVGLoader();
+
+	private final static Map<Key, Object> RENDERING_HINTS = Map.of( //
+			KEY_ANTIALIASING, VALUE_ANTIALIAS_ON, //
+			KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY, //
+			KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY, //
+			KEY_DITHERING, VALUE_DITHER_DISABLE, //
+			KEY_FRACTIONALMETRICS, VALUE_FRACTIONALMETRICS_ON, //
+			KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC, //
+			KEY_RENDERING, VALUE_RENDER_QUALITY, //
+			KEY_STROKE_CONTROL, VALUE_STROKE_PURE, //
+			KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON //
+	);
+
+	@Override
+	public ImageData[] rasterizeSVG(InputStream inputStream, int zoom) throws IOException {
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
user: login: HeikoKlare
body: Couldn't this method just return a single ImageData instead of an array? Is there any scenario in which it will return multiple data?
created_at: 2025-03-17T16:49:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal.image;
+
+import java.io.*;
+import java.nio.charset.*;
+import java.util.*;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.DPIUtil.*;
+
+/**
+ * A {@link FileFormat} implementation for handling SVG (Scalable Vector Graphics) files.
+ * <p>
+ * This class detects SVG files based on their header and uses a registered
+ * {@link SVGRasterizer} service to rasterize SVG content.
+ * </p>
+ */
+public class SVGFileFormat extends FileFormat {
+
+	/** The instance of the registered {@link SVGRasterizer}. */
+	private static final SVGRasterizer RASTERIZER = ServiceLoader.load(SVGRasterizer.class).findFirst().orElse(null);
+
+	@Override
+	boolean isFileFormat(LEDataInputStream stream) throws IOException {
+		byte[] firstBytes = new byte[5];
+		int bytesRead = stream.read(firstBytes);
+		stream.unread(firstBytes);
+		String header = new String(firstBytes, 0, bytesRead, StandardCharsets.UTF_8).trim();
+		return header.startsWith("<?xml") || header.startsWith("<svg");
+	}
+
+	@Override
+	List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom) {
+		return Arrays.stream(loadFromByteStream(100 * targetZoom / fileZoom)).map(d -> new ElementAtZoom<>(d, targetZoom)).toList();
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGFileFormat.java
user: login: HeikoKlare
body: I ask in another comment for whether the rasterizer cannot just return a single ImageData. In that case, I would merge the two `loadFromByteStream` methods and just do the transformation to a singleton list with one ElementAtZoom directly in that method.
created_at: 2025-03-17T16:50:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,144 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.KEY_ALPHA_INTERPOLATION;
+import static java.awt.RenderingHints.KEY_ANTIALIASING;
+import static java.awt.RenderingHints.KEY_COLOR_RENDERING;
+import static java.awt.RenderingHints.KEY_DITHERING;
+import static java.awt.RenderingHints.KEY_FRACTIONALMETRICS;
+import static java.awt.RenderingHints.KEY_INTERPOLATION;
+import static java.awt.RenderingHints.KEY_RENDERING;
+import static java.awt.RenderingHints.KEY_STROKE_CONTROL;
+import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING;
+import static java.awt.RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY;
+import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;
+import static java.awt.RenderingHints.VALUE_COLOR_RENDER_QUALITY;
+import static java.awt.RenderingHints.VALUE_DITHER_DISABLE;
+import static java.awt.RenderingHints.VALUE_FRACTIONALMETRICS_ON;
+import static java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC;
+import static java.awt.RenderingHints.VALUE_RENDER_QUALITY;
+import static java.awt.RenderingHints.VALUE_STROKE_PURE;
+import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints.Key;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.internal.image.SVGRasterizer;
+
+import com.github.weisj.jsvg.SVGDocument;
+import com.github.weisj.jsvg.geometry.size.FloatSize;
+import com.github.weisj.jsvg.parser.LoaderContext;
+import com.github.weisj.jsvg.parser.SVGLoader;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class uses the third party library JSVG for the raterization of SVG
+ * images.
+ * 
+ * @since 1.0.0
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
user: login: Michael5601
body: done.
created_at: 2025-03-18T08:35:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal.image;
+
+import java.io.*;
+import java.nio.charset.*;
+import java.util.*;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.DPIUtil.*;
+
+/**
+ * A {@link FileFormat} implementation for handling SVG (Scalable Vector Graphics) files.
+ * <p>
+ * This class detects SVG files based on their header and uses a registered
+ * {@link SVGRasterizer} service to rasterize SVG content.
+ * </p>
+ */
+public class SVGFileFormat extends FileFormat {
+
+	/** The instance of the registered {@link SVGRasterizer}. */
+	private static final SVGRasterizer RASTERIZER = ServiceLoader.load(SVGRasterizer.class).findFirst().orElse(null);
+
+	@Override
+	boolean isFileFormat(LEDataInputStream stream) throws IOException {
+		byte[] firstBytes = new byte[5];
+		int bytesRead = stream.read(firstBytes);
+		stream.unread(firstBytes);
+		String header = new String(firstBytes, 0, bytesRead, StandardCharsets.UTF_8).trim();
+		return header.startsWith("<?xml") || header.startsWith("<svg");
+	}
+
+	@Override
+	List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom) {
+		return Arrays.stream(loadFromByteStream(100 * targetZoom / fileZoom)).map(d -> new ElementAtZoom<>(d, targetZoom)).toList();
+	}
+
+	private ImageData[] loadFromByteStream(int targetZoom) {
+		try {
+			if (RASTERIZER != null && targetZoom != 0) {
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGFileFormat.java
user: login: Michael5601
body: done.
created_at: 2025-03-18T08:35:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,144 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.KEY_ALPHA_INTERPOLATION;
+import static java.awt.RenderingHints.KEY_ANTIALIASING;
+import static java.awt.RenderingHints.KEY_COLOR_RENDERING;
+import static java.awt.RenderingHints.KEY_DITHERING;
+import static java.awt.RenderingHints.KEY_FRACTIONALMETRICS;
+import static java.awt.RenderingHints.KEY_INTERPOLATION;
+import static java.awt.RenderingHints.KEY_RENDERING;
+import static java.awt.RenderingHints.KEY_STROKE_CONTROL;
+import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING;
+import static java.awt.RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY;
+import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;
+import static java.awt.RenderingHints.VALUE_COLOR_RENDER_QUALITY;
+import static java.awt.RenderingHints.VALUE_DITHER_DISABLE;
+import static java.awt.RenderingHints.VALUE_FRACTIONALMETRICS_ON;
+import static java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC;
+import static java.awt.RenderingHints.VALUE_RENDER_QUALITY;
+import static java.awt.RenderingHints.VALUE_STROKE_PURE;
+import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints.Key;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.internal.image.SVGRasterizer;
+
+import com.github.weisj.jsvg.SVGDocument;
+import com.github.weisj.jsvg.geometry.size.FloatSize;
+import com.github.weisj.jsvg.parser.LoaderContext;
+import com.github.weisj.jsvg.parser.SVGLoader;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class uses the third party library JSVG for the raterization of SVG
+ * images.
+ * 
+ * @since 1.0.0
+ */
+public class JSVGRasterizer implements SVGRasterizer {
+
+	private static final SVGLoader SVG_LOADER = new SVGLoader();
+
+	private final static Map<Key, Object> RENDERING_HINTS = Map.of( //
+			KEY_ANTIALIASING, VALUE_ANTIALIAS_ON, //
+			KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY, //
+			KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY, //
+			KEY_DITHERING, VALUE_DITHER_DISABLE, //
+			KEY_FRACTIONALMETRICS, VALUE_FRACTIONALMETRICS_ON, //
+			KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC, //
+			KEY_RENDERING, VALUE_RENDER_QUALITY, //
+			KEY_STROKE_CONTROL, VALUE_STROKE_PURE, //
+			KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON //
+	);
+
+	@Override
+	public ImageData[] rasterizeSVG(InputStream inputStream, int zoom) throws IOException {
+		SVGDocument svgDocument = loadSVG(inputStream);
+		if (svgDocument != null) {
+			return generateRasterizedImageData(svgDocument, zoom);
+		} else {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		}
+		return null;
+	}
+
+	private SVGDocument loadSVG(InputStream inputStream) {
+		return SVG_LOADER.load(inputStream, null, LoaderContext.createDefault());
+	}
+
+	private ImageData[] generateRasterizedImageData(SVGDocument svgDocument, int zoom) {
+		BufferedImage rasterizedImage = renderSVG(svgDocument, zoom);
+		return convertToSWTImageData(rasterizedImage);
+	}
+
+	private BufferedImage renderSVG(SVGDocument svgDocument, int zoom) {
+		float scalingFactor = zoom / 100.0f;
+		BufferedImage image = createImageBase(svgDocument, scalingFactor);
+		Graphics2D g = configureRenderingOptions(scalingFactor, image);
+		svgDocument.render(null, g);
+		g.dispose();
+		return image;
+	}
+
+	private BufferedImage createImageBase(SVGDocument svgDocument, float scalingFactor) {
+		FloatSize sourceImageSize = svgDocument.size();
+		int targetImageWidth = calculateTargetWidth(scalingFactor, sourceImageSize);
+		int targetImageHeight = calculateTargetHeight(scalingFactor, sourceImageSize);
+		return new BufferedImage(targetImageWidth, targetImageHeight, BufferedImage.TYPE_INT_ARGB);
+	}
+
+	private int calculateTargetWidth(float scalingFactor, FloatSize sourceImageSize) {
+		double sourceImageWidth = sourceImageSize.getWidth();
+		return (int) Math.round(sourceImageWidth * scalingFactor);
+	}
+
+	private int calculateTargetHeight(float scalingFactor, FloatSize sourceImageSize) {
+		double sourceImageHeight = sourceImageSize.getHeight();
+		return (int) Math.round(sourceImageHeight * scalingFactor);
+	}
+
+	private Graphics2D configureRenderingOptions(float scalingFactor, BufferedImage image) {
+		Graphics2D g = image.createGraphics();
+		g.setRenderingHints(RENDERING_HINTS);
+		g.scale(scalingFactor, scalingFactor);
+		return g;
+	}
+	
+	private ImageData[] convertToSWTImageData(BufferedImage rasterizedImage) {
+		int width = rasterizedImage.getWidth();
+	    int height = rasterizedImage.getHeight();
+		int[] pixels = ((DataBufferInt) rasterizedImage.getRaster().getDataBuffer()).getData();
+		PaletteData paletteData = new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF);
+        ImageData imageData = new ImageData(width, height, 32, paletteData);
+        int index = 0;
+        for (int y = 0; y < imageData.height; y++) {
+			for (int x = 0; x < imageData.width; x++) {
+				int alpha = (pixels[index] >> 24) & 0xFF;
+				imageData.setAlpha(x, y, alpha);
+				imageData.setPixel(x, y, pixels[index++]);
+			}
+		}
+        return new ImageData[]{imageData};
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
user: login: Michael5601
body: done.
created_at: 2025-03-18T08:36:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal.image;
+
+import java.io.*;
+import java.nio.charset.*;
+import java.util.*;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.DPIUtil.*;
+
+/**
+ * A {@link FileFormat} implementation for handling SVG (Scalable Vector Graphics) files.
+ * <p>
+ * This class detects SVG files based on their header and uses a registered
+ * {@link SVGRasterizer} service to rasterize SVG content.
+ * </p>
+ */
+public class SVGFileFormat extends FileFormat {
+
+	/** The instance of the registered {@link SVGRasterizer}. */
+	private static final SVGRasterizer RASTERIZER = ServiceLoader.load(SVGRasterizer.class).findFirst().orElse(null);
+
+	@Override
+	boolean isFileFormat(LEDataInputStream stream) throws IOException {
+		byte[] firstBytes = new byte[5];
+		int bytesRead = stream.read(firstBytes);
+		stream.unread(firstBytes);
+		String header = new String(firstBytes, 0, bytesRead, StandardCharsets.UTF_8).trim();
+		return header.startsWith("<?xml") || header.startsWith("<svg");
+	}
+
+	@Override
+	List<ElementAtZoom<ImageData>> loadFromByteStream(int fileZoom, int targetZoom) {
+		return Arrays.stream(loadFromByteStream(100 * targetZoom / fileZoom)).map(d -> new ElementAtZoom<>(d, targetZoom)).toList();
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGFileFormat.java
user: login: Michael5601
body: I merged the two methods and changed `Arrays.stream().map().toList()` to `List.of()` as now the Rasterizer does  directly return `ImageData` instead of `ImageData[]`. Way cleaner now :).
created_at: 2025-03-18T08:39:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,144 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.svg;
+
+import static java.awt.RenderingHints.KEY_ALPHA_INTERPOLATION;
+import static java.awt.RenderingHints.KEY_ANTIALIASING;
+import static java.awt.RenderingHints.KEY_COLOR_RENDERING;
+import static java.awt.RenderingHints.KEY_DITHERING;
+import static java.awt.RenderingHints.KEY_FRACTIONALMETRICS;
+import static java.awt.RenderingHints.KEY_INTERPOLATION;
+import static java.awt.RenderingHints.KEY_RENDERING;
+import static java.awt.RenderingHints.KEY_STROKE_CONTROL;
+import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING;
+import static java.awt.RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY;
+import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;
+import static java.awt.RenderingHints.VALUE_COLOR_RENDER_QUALITY;
+import static java.awt.RenderingHints.VALUE_DITHER_DISABLE;
+import static java.awt.RenderingHints.VALUE_FRACTIONALMETRICS_ON;
+import static java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC;
+import static java.awt.RenderingHints.VALUE_RENDER_QUALITY;
+import static java.awt.RenderingHints.VALUE_STROKE_PURE;
+import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints.Key;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.internal.image.SVGRasterizer;
+
+import com.github.weisj.jsvg.SVGDocument;
+import com.github.weisj.jsvg.geometry.size.FloatSize;
+import com.github.weisj.jsvg.parser.LoaderContext;
+import com.github.weisj.jsvg.parser.SVGLoader;
+
+/**
+ * A rasterizer implementation for converting SVG data into rasterized images.
+ * This class uses the third party library JSVG for the raterization of SVG
+ * images.
+ * 
+ * @since 1.0.0
+ */
+public class JSVGRasterizer implements SVGRasterizer {
+
+	private static final SVGLoader SVG_LOADER = new SVGLoader();
+
+	private final static Map<Key, Object> RENDERING_HINTS = Map.of( //
+			KEY_ANTIALIASING, VALUE_ANTIALIAS_ON, //
+			KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY, //
+			KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY, //
+			KEY_DITHERING, VALUE_DITHER_DISABLE, //
+			KEY_FRACTIONALMETRICS, VALUE_FRACTIONALMETRICS_ON, //
+			KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC, //
+			KEY_RENDERING, VALUE_RENDER_QUALITY, //
+			KEY_STROKE_CONTROL, VALUE_STROKE_PURE, //
+			KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON //
+	);
+
+	@Override
+	public ImageData[] rasterizeSVG(InputStream inputStream, int zoom) throws IOException {
path: bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
user: login: Michael5601
body: done.
created_at: 2025-03-18T08:39:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal.image;
+
+import java.io.*;
+
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.130
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGRasterizer.java
user: login: HannesWell
body: For internal classes the `since` tag is not necessary.
created_at: 2025-03-23T20:03:45Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH and others.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal.image;
+
+import java.io.*;
+
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 3.130
path: bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/SVGRasterizer.java
user: login: Michael5601
body: done. :)
created_at: 2025-03-24T08:18:36Z
author_association: CONTRIBUTOR

PR comments:
user: login: BeckerWdf
created_at: 2024-12-06T13:56:44Z
body: > 2\. PNG files cannot yet be deleted, as some are referenced across bundles via hardcoded paths e.g. in platform code. Removing these files would result in errors.

We could clean up (most of) these references across bundles so that we can delete (most of) the PNGs.
author_association: MEMBER
user: login: HannesWell
created_at: 2024-12-07T14:29:41Z
body: Thank you @Michael5601 for this PR I'm really looking forward to this.

Withouth having it tried out yet, I have a few remarks/questions/suggestions, all based on the assumption that providing support to render vector graphics is an 'implementation' detail that does not need any or only little new APIs where technically necessary (e.g. to specify a zoom factor).

Therefore I wonder for example a new public API type `ISVGRasterizer` is necessary or if it should better be an internal class, just like the other new types.
I assume it's not intended to allow external contributions of custom rasterizers? And as far as I understood our discussion, on the long run SVG support could become fully built in in SWT and then we have to decide again about the way JSVG is integrated.

Splitting the steps to handle all kind of icons sounds very reasonable for me. I think this should just focus on adding support for rendering vector graphics, just as it's done.

> 1. **Fragment Approach**:
>        Adding a fragment to SWT with the JSVG dependency failed, as SWT itself is composed of fragments, and fragments cannot depend on other fragments.

It's correct that Fragments can't have own Fragments, but a (host) bundle can have an arbitrary number of fragments.
So it would be possible to make `o.e.swt.svg` another fragment of `o.e.swt` and since all fragments and the their host bundle share the same classloader code from the corresponding native SWT fragments can then access classes from the svg fragment.
Then you could use the plain Java ServiceLoader without further addo or even just load a fixed class reflectivly using `Class.forName()`. This would avoid the need for adding a `SVGRasterizerRegistry` and making that the registration happens and happens early enough.

Setting up the java ServiceLoader between OSGi bundles (not fragments) on the other hand is a bit more complicated, but also possible. Just for reference, this blog post should explain it (I don't want to go into the details here): 
https://blog.osgi.org/2013/02/javautilserviceloader-in-osgi.html
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2024-12-07T14:37:00Z
body: ## Test Results
   539 files   -  2     539 suites   - 2   28m 17s ⏱️ - 1m 53s
 4 328 tests  - 35   4 314 ✅  - 33   13 💤  - 3  1 ❌ +1 
16 563 runs   - 29  16 451 ✅  - 27  111 💤  - 3  1 ❌ +1 

For more details on these failures, see [this check](https://github.com/eclipse-platform/eclipse.platform.swt/runs/39330618290).

Results for commit 0ef8783c. ± Comparison against base commit 3e596971.

<details>
  <summary>This pull request <b>removes</b> 37 and <b>adds</b> 2 tests. <i>Note that renamed tests count towards both.</i></summary>

```
AllWin32Tests org.eclipse.swt.graphics.ImageWin32Tests ‑ testImageDataForDifferentFractionalZoomsShouldBeDifferent
AllWin32Tests org.eclipse.swt.graphics.ImageWin32Tests ‑ testImageShouldHaveDimesionAsPerZoomLevel
AllWin32Tests org.eclipse.swt.tests.win32.Test_org_eclipse_swt_dnd_DND ‑ testByteArrayTransfer
AllWin32Tests org.eclipse.swt.tests.win32.Test_org_eclipse_swt_dnd_DND ‑ testFileTransfer
AllWin32Tests org.eclipse.swt.tests.win32.Test_org_eclipse_swt_dnd_DND ‑ testHtmlTransfer
AllWin32Tests org.eclipse.swt.tests.win32.Test_org_eclipse_swt_dnd_DND ‑ testImageTransfer_fromCopiedImage
AllWin32Tests org.eclipse.swt.tests.win32.Test_org_eclipse_swt_dnd_DND ‑ testImageTransfer_fromImage
AllWin32Tests org.eclipse.swt.tests.win32.Test_org_eclipse_swt_dnd_DND ‑ testImageTransfer_fromImageData
AllWin32Tests org.eclipse.swt.tests.win32.Test_org_eclipse_swt_dnd_DND ‑ testImageTransfer_fromImageDataFromImage
AllWin32Tests org.eclipse.swt.tests.win32.Test_org_eclipse_swt_dnd_DND ‑ testRtfTransfer
…
```

```
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_internal_SVGRasterizer ‑ test_ConstructorLorg_eclipse_swt_graphics_Device_ImageDataProvider
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_internal_SVGRasterizer ‑ test_ConstructorLorg_eclipse_swt_graphics_Device_ImageFileNameProvider
```
</details>

[test-results]:data:application/gzip;base64,H4sIAMTc4WcC/03Nyw7CIBCF4VdpWLtgYArFlzE4hYTYi+GyMr67tGmxy/+bcPgwHyaX2L3rpbl1LJWQLzmWaHNYlwqgjK5Sr3m7oxTDmY9UiHYD/NsrvLdnsom3YdqkgYtxjVV4lViWtP/SK3nkOQsKe2h2zEKTy+ze11Va5znkGow7P+hBkvQGhcYnRwMkRq9BkhsVaSuqc2TfH5PE0iMTAQAA

:recycle: This comment has been updated with latest results.
author_association: CONTRIBUTOR
user: login: Michael5601
created_at: 2024-12-09T12:21:23Z
body: 
Thank you for the comment, I will check all your remarks soon. 

> Setting up the java ServiceLoader between OSGi bundles (not fragments) on the other hand is a bit more complicated, but also possible. Just for reference, this blog post should explain it (I don't want to go into the details here): https://blog.osgi.org/2013/02/javautilserviceloader-in-osgi.html

To this point I can already say that I tried it with the same article but I wasn't successful. 
author_association: CONTRIBUTOR
user: login: HeikoKlare
created_at: 2024-12-10T09:00:11Z
body: > Therefore I wonder for example a new public API type `ISVGRasterizer` is necessary or if it should better be an internal class, just like the other new types. I assume it's not intended to allow external contributions of custom rasterizers? And as far as I understood our discussion, on the long run SVG support could become fully built in in SWT and then we have to decide again about the way JSVG is integrated.

That's something we should discuss and agree on. I would not in favor of tightly integrating a specific SVG rasterizer into SWT. Having it interchangeable ensures low coupling and gives the flexibility to exchange it without rebuilding SWT (by just providing some other fragment/bundle/... that contains an alternative rasterizer implementation). We could still think of making that interface internal for now, so that the interface is not public but in case you would want to provide a different rasterizer it would still be possible (with potentially a warning).
 
> > 1. **Fragment Approach**:
> >    Adding a fragment to SWT with the JSVG dependency failed, as SWT itself is composed of fragments, and fragments cannot depend on other fragments.
> 
> It's correct that Fragments can't have own Fragments, but a (host) bundle can have an arbitrary number of fragments. So it would be possible to make `o.e.swt.svg` another fragment of `o.e.swt` and since all fragments and the their host bundle share the same classloader code from the corresponding native SWT fragments can then access classes from the svg fragment. Then you could use the plain Java ServiceLoader without further addo or even just load a fixed class reflectivly using `Class.forName()`. This would avoid the need for adding a `SVGRasterizerRegistry` and making that the registration happens and happens early enough.

For some more clarifiation on using a fragment: If we are not mistaken, to provide a fragment with a concrete rasterizer implementation, the rasterizer interface needs to be placed directly in the SWT host bundle, not in some common package that is mapped into all the native fragments like done for all the other SWT code. Otherwise, the SVG fragment would be tied to the presence of another fragment providing that interface, which (reasonably) seems to be prohibited. As a consequence, that interface would become the first source element to be placed directly in the SWT host bundle, while all the other code is placed in the native fragments. In addition, we need to reference that interface from within the native fragments (to make use a rasterizer in SWT code). With the current configuration of the bundles/fragment, this is not possible. The SWT host bundle has to be added to the classpath of the native fragments to access the interface via adding the PDE required plugins container to its classpath (i.e., to add the classpath entry `<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>`). All this may not be prohitibitive, but it's all changes to the current configuration of the SWT host and fragments that need to be considered.

> Setting up the java ServiceLoader between OSGi bundles (not fragments) on the other hand is a bit more complicated, but also possible. Just for reference, this blog post should explain it (I don't want to go into the details here): https://blog.osgi.org/2013/02/javautilserviceloader-in-osgi.html

I just want to add that I also tried to create a minimal reproducer for using a ServiceLoader across OSGi bundles, but I also failed, just like Michael. The necessary capabilities added to the manifests do not even show up when listing the capabilities of the bundles via the OSGi console. Probably, I am / we are doing something wrong here.
author_association: CONTRIBUTOR
user: login: Michael5601
created_at: 2024-12-10T09:44:46Z
body: I edited the PR message and added the following points:

1. **Planned Tasks for this PR**: I will include regression tests and I will perform a performance evaluation. The feature also needs to be tested for gtk and cocoa as they have different `ImageLoader` implementations.
2. **New Outstanding issue**: There are missing or broken SVGs that do not need to be fixed for this feature to work but if we plan to remove all PNGs finally this issue needs to be resolved. 
author_association: CONTRIBUTOR
user: login: Michael5601
created_at: 2024-12-11T16:18:41Z
body: Please see this [Draft](https://github.com/eclipse-platform/eclipse.platform.swt/pull/1647) for the Follow-up-functionality for the automatic customization of graphically customized icons as announced previously in this PR. 

The functionality is complete but the draft is meant as a base for discussion. All changes to this PR will be done also in the draft.
author_association: CONTRIBUTOR
user: login: HannesWell
created_at: 2024-12-11T22:40:40Z
body: > > Therefore I wonder for example a new public API type `ISVGRasterizer` is necessary or if it should better be an internal class, just like the other new types. I assume it's not intended to allow external contributions of custom rasterizers? And as far as I understood our discussion, on the long run SVG support could become fully built in in SWT and then we have to decide again about the way JSVG is integrated.
> 
> That's something we should discuss and agree on. 

Yes, absolutely. I suggest we discuss this in a separate issue, in parallel to this PR.

> > Setting up the java ServiceLoader between OSGi bundles (not fragments) on the other hand is a bit more complicated, but also possible. Just for reference, this blog post should explain it (I don't want to go into the details here): https://blog.osgi.org/2013/02/javautilserviceloader-in-osgi.html
> 
> I just want to add that I also tried to create a minimal reproducer for using a ServiceLoader across OSGi bundles, but I also failed, just like Michael. The necessary capabilities added to the manifests do not even show up when listing the capabilities of the bundles via the OSGi console. Probably, I am / we are doing something wrong here.

Besides adding the correct required and provided capabilities to the provider and consumer of the service (implementation), you also have to make sure that `aries.spifly` as well as the provider of the service (implementation) are auto-started at an appropriated level, see for example how it's done for `slf4j-simple` providing a `logging-provider` consumed by the `slf4j-api` bundle:
https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/blob/02e51912906b367d441fb0d120c7fd3bc3990dad/eclipse.platform.releng.tychoeclipsebuilder/eclipse.platform.repository/sdk.product#L193-L200

Everyone building products or launching Eclipse-apps from a workspace that include this SVG support for SWT would have to configure this. This would IMHO be cumbersome and to avoid all these difficulties I strongly would like to avoid the need for the OSGI Service Loader mediator in this case.

> 
> > > 1. **Fragment Approach**:
> > >    Adding a fragment to SWT with the JSVG dependency failed, as SWT itself is composed of fragments, and fragments cannot depend on other fragments.
> > 
> > 
> > It's correct that Fragments can't have own Fragments, but a (host) bundle can have an arbitrary number of fragments. So it would be possible to make `o.e.swt.svg` another fragment of `o.e.swt` and since all fragments and the their host bundle share the same classloader code from the corresponding native SWT fragments can then access classes from the svg fragment. Then you could use the plain Java ServiceLoader without further addo or even just load a fixed class reflectivly using `Class.forName()`. This would avoid the need for adding a `SVGRasterizerRegistry` and making that the registration happens and happens early enough.
> 
> For some more clarifiation on using a fragment: If we are not mistaken, to provide a fragment with a concrete rasterizer implementation, the rasterizer interface needs to be placed directly in the SWT host bundle, not in some common package that is mapped into all the native fragments like done for all the other SWT code.

That's right. The only disadvantage I can think of for having code in the `o.e.swt` host bundle to, would be that for plain-Java/non-OSGi apps `o.e.swt` would then also need `o.e.swt` on the classpath, while they only need the native fragment of the current platform now. So I guess that wouldn't be ideal, altough I don't know if it's forbidden by a rule.
But maybe we could make that change only for the compilation in the workspace, because at runtime because all fragments and their host share the same classloader, the actual location of a class-file among them, is irrelevant (maybe with multiple occurrences of the same class).
But it would also lead to another problem: Because `SVGRasterizer` returns an `ImageData` object that class must be moved to o.e.swt too and with it it's whole dependency-closure and eventually we would more or less revert:
- https://github.com/eclipse-platform/eclipse.platform.swt/pull/1008

But I tried a few things out and had another idea:
If `o.e.swt.svg` is a fragment of `o.e.swt`, we could simply add an explicit requirement to the native fragments to the build-path of `o.e.swt.svg`. See the details below in the code-remarks.
But that should be a solution that seems feasible.


~~Another option would be to not provide an interface `SVGRasterizer` at all and just load the class `JSVGRasterizer` reflectively using `Class.forName()` and also calling all relevant methods reflectively (or a method handle to improve performance).
This way a special interface to implement would not be necessary at all, but it would also be a bit more 'bare-metal'.~~
Edit: Thinking about this again, this wouldn't save much since e.g. the `ImageData` type is still required, which leads to the same problem again.

Yet another option would be to avoid a new `o.e.swt.svg` bundle/fragment and add all code  to interact with JSVG, which is not that much, directly into o.e.swt respectivly it's native fragments and make the dependency to JSVG optional and be prepared for it's absence! This way one would only have to add jsvg to an application/product for OSGi-runtimes and plain Java apps and not also `o.e.swt.svg`. Hopefully it has OSGi metadata soon:
- https://github.com/weisJ/jsvg/pull/107

Of course this would more or less make it impossible to supply alternative rasterizer. But I cannot tell if it's realistic that custom ones will be supplied or not and we are just over-engineering here? 
Of course it shouldn't be integrated to tightly, so that an exchange in the future is feasible if necessary. But that's maybe part of the discussion of the very first point of this comment.
author_association: MEMBER
user: login: laeubi
created_at: 2024-12-12T07:08:28Z
body: > For some more clarifiation on using a fragment: If we are not mistaken, to provide a fragment with a concrete rasterizer implementation, the rasterizer interface needs to be placed directly in the SWT host bundle, not in some common package that is mapped into all the native fragments like done for all the other SWT code.

This is not true, all fragments are merged with the host and therefore you have one big class space, it might be that the IDE (or Tycho) currently have some limitations in this regard but we could/should fix that instead of making things harder than required.

A `ServiceLoader` approach seem suitable also here for me, no need to complicate the thing with OSGi SPI, this is only for applications that are not aware of OSGi at all and this is not the case here and also not needed.
author_association: CONTRIBUTOR
user: login: Michael5601
created_at: 2024-12-12T11:02:09Z
body: > 
> Yes, absolutely. I suggest we discuss this in a separate issue, in parallel to this PR.
> 
I agree. I created this [discussion](https://github.com/eclipse-platform/eclipse.platform.swt/discussions/1651) for this topic. 


author_association: CONTRIBUTOR
user: login: Michael5601
created_at: 2024-12-12T14:01:19Z
body: I just realised that cocoa and gtk not only have different implementation for `ImageLoader` but also for the `Image` class. I can of course also implement my changes there but right now I can't test the changes. 
author_association: CONTRIBUTOR
user: login: Michael5601
created_at: 2024-12-21T14:10:44Z
body: ## Performance Measurement for Eclipse UI Startup with New Feature

As announced, I performed a performance measurement for this feature. For this, I measured the time of starting the Eclipse UI with and without the feature by printing the time in the console at the following positions:

- **Start time**: Right before the call of `PlatformUI.createAndRunWorkbench()` inside of `IDEApplication.start()`.
- **End time**: Right after the first call of `IDEWorkbenchAdvisor.eventLoopIdle()` inside of `PartRenderingEngine.run()`.

### Measurement Modes

I measured the time for the following modes:

- **PNG 100%**: Current Implementation (PNG) with deactivated scaling. Icons of size 100% will be loaded.
- **SVG 100%**: SVG feature implementation with deactivated scaling. Icons will be rasterized in 100% size.
- **PNG 125%**: Current Implementation (PNG) with activated scaling. Icons will be loaded in 200% and then scaled down to 125%.
- **SVG 125%**: SVG feature implementation with activated scaling. Icons will be rasterized in 125% size.

Each mode was measured **10 times**. All 40 data points were measured back-to-back, and no other application except Eclipse itself was open during the measurement.

### Results

The results can be seen in the following boxplot and table:

![Performance Measurement Boxplot](https://github.com/user-attachments/assets/b099015c-1923-4a38-bb37-bc1a969f5524)

| Groups      | PNG 100% | PNG 125% | SVG 100% | SVG 125% |
|-------------|-----------|----------|----------|----------|
| **Minimum** | 9.416     | 9.299    | 9.601    | 10.065   |
| **Median**  | 9.529     | 9.5555   | 9.9035   | 10.3165  |
| **Maximum** | 9.93      | 9.929    | 10.274   | 10.549   |
| **Mean**    | 9.5589    | 9.6232   | 9.9053   | 10.3092  |

The results show that the new feature adds an extra startup time of around **0.4 - 0.8 seconds**.

### Potential Inaccuracies and Automation Challenges

Please be aware of the potential inaccuracies due to manual measurement. I attempted to automate the measurement but encountered issues with the following JUnit test. Every JUnit plugin test runs with a `Workbench`, and since the `Workbench` is a singleton, no other workbenches can be started simultaneously. If you have a solution for this, please let me know.

```java
public class EclipseStartUpTest {

    static long endTime;

    @Test
    public void testStandardUIStartupTime() throws Exception {
        endTime = 0;
        long startTime = System.currentTimeMillis();
        PlatformUI.createAndRunWorkbench(PlatformUI.createDisplay(), new WorkbenchAdvisor() {

            @Override
            public void eventLoopIdle(Display display) {
                if (endTime == 0) {
                    endTime = System.currentTimeMillis();
                }
            }

            @Override
            public String getInitialWindowPerspectiveId() {
                return "org.eclipse.ui.resourcePerspective";
            }
        });
        long elapsedTime = endTime - startTime;
        System.out.println("Eclipse UI startup time: " + elapsedTime + " ms");
    }
}

author_association: CONTRIBUTOR
user: login: Michael5601
created_at: 2024-12-22T10:42:17Z
body: I added a new regression test for this feature in commit 07f860cf9e8240752c4860d84201823c7a5ca0aa. Right now this test does not work as the SVGRasterizer-Fragment ist not loaded at runtime, so no SVGRasterizer can be returned. 

@HannesWell Do you know what I need to include for it to be loaded? I thought this should happen as soon as SWT is used. 
author_association: CONTRIBUTOR
user: login: Michael5601
created_at: 2024-12-22T11:17:50Z
body: I created a [discussion](https://github.com/eclipse-platform/eclipse.platform.swt/discussions/1693) for the autoscaling-flag of SWT as mentioned in the community call lately.
author_association: CONTRIBUTOR
user: login: BeckerWdf
created_at: 2024-12-23T08:53:47Z
body: Thanks for the numbers. I have one question:

It looks like scaling the PNGs almost costs nothing ( 9.529  compared to 9.5555) But scaling the SVGs is more expensive ( 9.9035 compared to 10.3165). Why is this the case?
author_association: MEMBER
user: login: Michael5601
created_at: 2024-12-26T10:44:06Z
body: > It looks like scaling the PNGs almost costs nothing ( 9.529 compared to 9.5555) 

Scaling the PNGs is pretty cheap performance wise. I am also surprised by this. 

> But scaling the SVGs is more expensive ( 9.9035 compared to 10.3165). Why is this the case?

Rendering SVGs in a higher resolution is more expensive performance wise. For JSVG this effect is the smallest from all libraries that I analyzed but it is still noticeable. 

I can also imagine that the conversion from `BufferedImage` to SWT `ImageData` is more expensive when increasing the image size. 
author_association: CONTRIBUTOR
user: login: BeckerWdf
created_at: 2024-12-27T10:23:14Z
body: > > It looks like scaling the PNGs almost costs nothing ( 9.529 compared to 9.5555)
> 
> Scaling the PNGs is pretty cheap performance wise. I am also surprised by this.
> 
> > But scaling the SVGs is more expensive ( 9.9035 compared to 10.3165). Why is this the case?
> 
> Rendering SVGs in a higher resolution is more expensive performance wise. For JSVG this effect is the smallest from all libraries that I analyzed but it is still noticeable.
> 
> I can also imagine that the conversion from `BufferedImage` to SWT `ImageData` is more expensive when increasing the image size.

Can we somehow improve on this?
author_association: MEMBER
user: login: Michael5601
created_at: 2025-01-07T14:07:40Z
body: > I added a new regression test for this feature in commit [07f860c](https://github.com/eclipse-platform/eclipse.platform.swt/commit/07f860cf9e8240752c4860d84201823c7a5ca0aa). Right now this test does not work as the SVGRasterizer-Fragment ist not loaded at runtime, so no SVGRasterizer can be returned.
> 
> @HannesWell Do you know what I need to include for it to be loaded? I thought this should happen as soon as SWT is used.

The reason for my problem was that I did not run the test as a JUnit Plugin Test. Sadly the JUnit Plugin Test lead to a new problem as loading the icons from the test plugin was difficult. I moved the test to a new plugin and changed the loading mechanism. Now it works.  

See d084f7d78e365ec63f6989dc3a03495f3f62cc76 for the commit.
author_association: CONTRIBUTOR
user: login: Michael5601
created_at: 2025-01-14T10:07:19Z
body: ### Performance Test Results Update

I have automated the performance tests, making these results more reliable than the initial measurements in https://github.com/eclipse-platform/eclipse.platform.swt/pull/1638#issuecomment-2558132963. I plan to discuss this update during Thursday's community call, where it can serve as a basis for our discussion.

---

### Measurement Modes

The performance tests measured the following scenarios:

- **PNG 100%**: Current implementation with native scaling at 100%. Icons are loaded at 16x16px.
- **SVG 100%**: SVG-based implementation with native scaling at 100%. Icons are rasterized to 16x16px.
- **PNG 125%**: Current implementation with native scaling at 125%. Icons are loaded at 200% (32x32px) and scaled down to 20x20px.
- **SVG 125%**: SVG-based implementation with native scaling at 125%. Icons are rasterized to 20x20px.
- **PNG 200%**: Current implementation with native scaling at 200%. Icons are loaded at 32x32px.
- **SVG 200%**: SVG-based implementation with native scaling at 200%. Icons are rasterized to 32x32px.

Each mode was measured 50 times using an automated JUnit test. Each test involved loading 105 icons from the default Java perspective.

---

### Results

The following results differ from the first measurement as they focus on UI startup time only rather than the full Eclipse application startup time.

![newplot (2)](https://github.com/user-attachments/assets/cbb740a8-47ce-4088-9b42-4db760a02f5d)

| Groups      | PNG 100% | SVG 100% | PNG 125% | SVG 125% | PNG 200% | SVG 200% |
|-------------|----------|----------|----------|----------|----------|----------|
| **Minimum** | 6.195    | 6.984    | 6.240    | 7.091    | 7.030    | 8.096    |
| **Median**  | 6.515    | 7.346    | 6.584    | 7.496    | 7.645    | 8.715    |
| **Mean**    | 6.523    | 7.359    | 6.617    | 7.495    | 7.619    | 8.706    |
| **Maximum** | 6.916    | 7.683    | 7.149    | 7.850    | 8.106    | 9.239    |

---

### Evaluation

The additional time required for SVGs, compared to PNGs, is as follows:

- **100% scaling**: +0.831s (12.75%)
- **125% scaling**: +0.912s (13.85%)
- **200% scaling**: +1.070s (13.99%)

I performed manual testing at 125% with the SVG feature to find out where the performance bottlenecks are.
- **BufferedImage to ImageData conversion**: ~0.1 seconds for 100 icons.
- **Initial rasterization overhead**: The first icon rasterization takes ~0.1 seconds. I think this due to library initialization. Subsequent rasterizations average ~5ms per icon, totaling ~0.6 seconds for 100 icons.

These factors account for ~0.7s of the 0.912s overhead for SVG 125, with the remaining time likely due to additional File I/O operations. The File I/O is likely higher for SVGs than for PNGs as the file size is bigger. 

---
### Optimization Ideas

To improve the performance, I thought of the following ideas:

1. **Reduce SVG file sizes**:
   - Remove unused elements in the SVGs (e.g., empty groups or elements outside the view area) to decrease file size and improve File I/O performance.

2. **Use a faster rasterization library**:
   - JSVG is significantly faster than Apache Batik (~2x) and EchoSVG (~3.5x) and also the fastest Java library I found. Non-Java libraries could further improve performance but would likely add complexity for integration.

3. **Simplify icon designs**:
   - 58.26% of SVG elements in Eclipse icons are used for color gradients. These gradients are often not seen at small sizes. Simplifying these to monochrome surfaces would reduce complexity and file size.

4. **Implement a caching mechanism**:
   - Caching rasterized icons (e.g., in the file system) for multiple eclipse applications could reduce the need for repeated rasterizations. However, the performance benefit depends on whether cached File I/O is faster than rasterization. The integration of a caching mechanism also leads to some complex challenges. 
author_association: CONTRIBUTOR
user: login: HannesWell
created_at: 2025-01-19T14:14:32Z
body: I'm also looking into the build failures and tried out a few things locally but didn't succeed. Will report back once I have found a working solution.
author_association: MEMBER
user: login: HannesWell
created_at: 2025-01-23T21:30:01Z
body: Now that we have a release of [JSVG 1.7.0](https://github.com/weisJ/jsvg/releases/tag/v1.7.0-rc1) with proper OSGi-metadata, it is added to the TP via:
- https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/pull/2776
author_association: MEMBER
user: login: Michael5601
created_at: 2025-02-24T12:56:44Z
body: FYI: @HannesWell @HeikoKlare 
As discussed I created an [issue](https://github.com/weisJ/jsvg/issues/114) in the JSVG repo to dicuss wether JSVG could return the rasterized image data as a `byte array` instead of `BufferedImage`. This way the current conversion in this PR could be simplified. 


author_association: CONTRIBUTOR
user: login: laeubi
created_at: 2025-02-24T13:26:00Z
body: A byte array would imply a certain image format, maybe an `int[][]` of RGBA pixels would be better suited.
author_association: CONTRIBUTOR
user: login: Michael5601
created_at: 2025-02-24T13:57:20Z
body: > A byte array would imply a certain image format, maybe an `int[][]` of RGBA pixels would be better suited.

yes `int[][]` would probably be even better. The author of JSVG did not have a fast answer to this. If I find some time I will investigate if there is a faster way for converting BufferedImage to SWT ImageData than currently implemented.
author_association: CONTRIBUTOR
user: login: Michael5601
created_at: 2025-02-25T09:48:04Z
body: I improved the conversion BufferedImage to SWT ImageData in commit d8282407be7cc5346702ba837d28d6d677ef5c17 by directly reading the whole `int[]` array of pixels from the BufferedImage and then setting every pixel in a SWT ImageData instance. 

This should be faster and removes 60 lines of code.
It should also work in every case as the BufferedImage is created with `TYPE_INT_ARGB`.
author_association: CONTRIBUTOR
user: login: HannesWell
created_at: 2025-03-02T23:10:01Z
body: > I improved the conversion BufferedImage to SWT ImageData in commit [d828240](https://github.com/eclipse-platform/eclipse.platform.swt/commit/d8282407be7cc5346702ba837d28d6d677ef5c17) by directly reading the whole `int[]` array of pixels from the BufferedImage and then setting every pixel in a SWT ImageData instance.
> 
> This should be faster and removes 60 lines of code.

That's great. Can you estimate any specific numbers? Respectively do you think it would be worth it to re-run you test-suite?

Regarding the build, after the reconfiguration and with https://github.com/eclipse-tycho/tycho/pull/4771 the build finally succeeds. I have squashed all my build related commits into your first commit as well as the move of the test-bundles and combined some your refactoring commits and force pushed it to this PR's branch.
With that I also added the new svg.tests bundle to the build (which was missing after the move).

You have probably noticed it already, but https://github.com/eclipse-platform/eclipse.platform.swt/pull/1828 has evolved a lot in the meantime, but leveraging its current state should simplify this PR quite a bit.
author_association: MEMBER
user: login: Michael5601
created_at: 2025-03-03T07:23:44Z
body: > That's great. Can you estimate any specific numbers? Respectively do you think it would be worth it to re-run you test-suite?
> 
As stated in this [comment](https://github.com/eclipse-platform/eclipse.platform.swt/pull/1638#issuecomment-2589502800) the time for the conversion from BufferedImage to ImageData is ~0.1 seconds for 100 icons (with ~0.8-1.0 seconds additional UI startup time when using the feature). So the simplification of the conversion that I provided won't yield a big reduction on the additional UI startup time and the main purpose is the reduction of the code amount and complexity. 

I don't think running the performance tests again will change the results significantly now so I would wait with this until we have further improvements to the performance.

> With that I also added the new svg.tests bundle to the build (which was missing after the move).
> 
I discussed this with @HeikoKlare and we decided to remove the additional test bundle as you suggested and move the tests into the existing bundle. I tried this but failed because the rasterizer wouldn't be loaded after test execution. I will investigate this and in case of success we can remove the test bundle from the build. 

> You have probably noticed it already, but https://github.com/eclipse-platform/eclipse.platform.swt/pull/1828 has evolved a lot in the meantime, but leveraging its current state should simplify this PR quite a bit.
> 
I plan to integrate the additional changes today and tomorrow. Thanks for pushing this forward. :)
author_association: CONTRIBUTOR
user: login: HeikoKlare
created_at: 2025-03-25T07:59:22Z
body: Thank you for your final improvements, Hannes!

All builds succeeded, the test report still contains a previous failure but of a flaky browser test, unrelated to these changes. Thus merging.
author_association: CONTRIBUTOR
user: login: Michael5601
created_at: 2025-03-25T16:06:58Z
body: I’m very grateful to the community for all the support over the past few months. A special thanks to @HeikoKlare and @HannesWell for your valuable help! I couldn’t have hoped for a better outcome for a project that began as part of my thesis, and I’m verry happy that this first part is now merged.
author_association: CONTRIBUTOR
user: login: iloveeclipse
created_at: 2025-03-26T07:18:28Z
body: @Michael5601, @HeikoKlare : This PR most likely requires some more work, please check https://github.com/eclipse-platform/eclipse.platform.swt/issues/1944
author_association: MEMBER
user: login: iloveeclipse
created_at: 2025-04-14T13:44:28Z
body: @Michael5601, @HeikoKlare : after the new bundle was made part of SDk package, we see test failure, please check https://github.com/eclipse-platform/eclipse.platform.swt/issues/2009.
author_association: MEMBER
user: login: HeikoKlare
created_at: 2025-04-16T15:48:31Z
body: > @Michael5601, @HeikoKlare : after the new bundle was made part of SDk package, we see test failure, please check #2009.

Resolved via https://github.com/eclipse-platform/eclipse.platform.swt/pull/2010
author_association: CONTRIBUTOR