diff --git a/diylc/diylc-swing/src/main/java/org/diylc/DIYLCStarter.java b/diylc/diylc-swing/src/main/java/org/diylc/DIYLCStarter.java
index fb02f9d0..5849545c 100644
--- a/diylc/diylc-swing/src/main/java/org/diylc/DIYLCStarter.java
+++ b/diylc/diylc-swing/src/main/java/org/diylc/DIYLCStarter.java
@@ -33,6 +33,7 @@ import org.diylc.appframework.miscutils.PropertyInjector;
import org.diylc.appframework.miscutils.Utils;
import org.diylc.swingframework.export.DrawingExporter;
import org.diylc.swingframework.fonts.FontOptimizer;
+import org.diylc.utils.DPIScalingUtils;
import com.thoughtworks.xstream.XStream;
import org.diylc.common.IPlugInPort;
import org.diylc.core.IView;
@@ -137,6 +138,12 @@ public class DIYLCStarter {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+
+ // Scale all UIManager font defaults for high DPI displays
+ // This applies globally to all Swing components, fixing font scaling
+ // issues on Windows with high DPI displays where HTML rendering may
+ // not properly scale fonts.
+ DPIScalingUtils.scaleUIManagerFonts();
} catch (Exception e) {
LOG.error("Could not set Look&Feel", e);
}
diff --git a/diylc/diylc-swing/src/main/java/org/diylc/utils/DPIScalingUtils.java b/diylc/diylc-swing/src/main/java/org/diylc/utils/DPIScalingUtils.java
new file mode 100644
index 00000000..47e973b0
--- /dev/null
+++ b/diylc/diylc-swing/src/main/java/org/diylc/utils/DPIScalingUtils.java
@@ -0,0 +1,346 @@
+/*
+ *
+ * DIY Layout Creator (DIYLC).
+ * Copyright (c) 2009-2025 held jointly by the individual authors.
+ *
+ * This file is part of DIYLC.
+ *
+ * DIYLC is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * DIYLC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with DIYLC. If not, see .
+ *
+ */
+package org.diylc.utils;
+
+import java.awt.Font;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.Toolkit;
+import java.util.Enumeration;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+
+/**
+ * Utility class for handling DPI scaling on high-resolution displays.
+ *
+ * This is particularly important on Windows where HTML rendering in Swing
+ * components (like JLabel) can fail to properly scale fonts on high DPI displays.
+ *
+ * @author Branislav Stojkovic
+ */
+public class DPIScalingUtils {
+
+ private static Double dpiScaleFactor = null;
+ private static final double SCALING_THRESHOLD = 0.85; // 85% threshold to avoid double-scaling
+ private static final double OVERSCALE_THRESHOLD = 1.3; // 130% - if font is this much larger than expected, it's over-scaled
+ private static final float BASE_FONT_SIZE_96DPI = 12.0f; // Standard font size at 96 DPI
+ private static final float MINIMUM_FONT_SIZE = 10.0f;
+ private static final float MAXIMUM_REASONABLE_FONT_SIZE = 20.0f;
+
+ /**
+ * Gets the DPI scale factor for the current display.
+ * This is particularly important on Windows with high DPI displays where
+ * HTML rendering in Swing components can fail to properly scale fonts.
+ *
+ * @return The DPI scale factor (1.0 for normal displays, > 1.0 for high DPI)
+ */
+ public static double getDpiScaleFactor() {
+ if (dpiScaleFactor == null) {
+ try {
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ GraphicsDevice gd = ge.getDefaultScreenDevice();
+ GraphicsConfiguration gc = gd.getDefaultConfiguration();
+
+ // Get the transform scale factor from GraphicsConfiguration
+ // This is the most reliable way to detect DPI scaling
+ double scaleX = gc.getDefaultTransform().getScaleX();
+ double scaleY = gc.getDefaultTransform().getScaleY();
+ double transformScale = Math.max(scaleX, scaleY);
+
+ // On Windows, Java's DPI awareness can be inconsistent, especially with HTML rendering
+ // Check if we're on Windows and if the transform indicates scaling
+ if (System.getProperty("os.name", "").toLowerCase().contains("windows")) {
+ // If transform scale is > 1.0, we have DPI scaling
+ // HTML rendering in Swing on Windows may not respect this, so we need to compensate
+ if (transformScale > 1.0) {
+ dpiScaleFactor = transformScale;
+ } else {
+ // Fallback: check screen resolution as a secondary indicator
+ int screenResolution = Toolkit.getDefaultToolkit().getScreenResolution();
+ if (screenResolution > 96) {
+ // High DPI display detected via resolution
+ dpiScaleFactor = screenResolution / 96.0;
+ } else {
+ dpiScaleFactor = 1.0;
+ }
+ }
+ } else {
+ // On non-Windows systems, trust the transform scale
+ dpiScaleFactor = Math.max(1.0, transformScale);
+ }
+ } catch (Exception e) {
+ // Fallback to 1.0 if there's any error
+ dpiScaleFactor = 1.0;
+ }
+ }
+ return dpiScaleFactor;
+ }
+
+ /**
+ * Calculates the expected font size for a given base size at the current DPI.
+ *
+ * @param baseSize The base font size (typically at 96 DPI)
+ * @return The expected font size at the current DPI
+ */
+ private static float getExpectedFontSize(float baseSize) {
+ double scaleFactor = getDpiScaleFactor();
+ return (float) (baseSize * scaleFactor);
+ }
+
+ /**
+ * Scales a font to match the DPI scaling of the display.
+ * This is useful for components that use HTML rendering (like JLabel with HTML text),
+ * which may not properly scale fonts on Windows with high DPI displays.
+ *
+ * @param font The font to scale
+ * @return A scaled font if high DPI is detected and scaling is needed, otherwise the original font
+ */
+ public static Font scaleFontForDPI(Font font) {
+ if (font == null) {
+ return null;
+ }
+
+ double scaleFactor = getDpiScaleFactor();
+
+ // Only apply scaling when high DPI is detected
+ if (scaleFactor <= 1.0) {
+ return font;
+ }
+
+ float currentSize = font.getSize();
+
+ // Calculate what the base size would be (assuming current size is at 96 DPI)
+ float assumedBaseSize = currentSize;
+
+ // Calculate what the expected size should be at this DPI
+ float expectedSize = getExpectedFontSize(assumedBaseSize);
+
+ // Calculate the ratio of current to expected size
+ double sizeRatio = currentSize / expectedSize;
+
+ // Only scale if the current size is significantly smaller than expected
+ // This prevents double-scaling on systems that already handle it correctly
+ if (sizeRatio < SCALING_THRESHOLD) {
+ // Scale to expected size, but ensure minimum readability
+ float scaledSize = Math.max(expectedSize, MINIMUM_FONT_SIZE);
+ return font.deriveFont(scaledSize);
+ }
+
+ return font;
+ }
+
+ /**
+ * Downscales a font that appears to be over-scaled.
+ * This calculates what the font size should be based on the actual DPI
+ * and scales it down proportionally if it's significantly larger than expected.
+ *
+ * @param font The font to downscale
+ * @return A downscaled font if needed, otherwise the original font
+ */
+ private static Font downscaleFontForDPI(Font font) {
+ if (font == null) {
+ return font;
+ }
+
+ float currentSize = font.getSize();
+ double scaleFactor = getDpiScaleFactor();
+
+ // Calculate what the base size would be if we reverse the DPI scaling
+ // This assumes the font was scaled from a base size at 96 DPI
+ float estimatedBaseSize = (float) (currentSize / scaleFactor);
+
+ // Calculate what the expected size should be at this DPI
+ float expectedSize = getExpectedFontSize(BASE_FONT_SIZE_96DPI);
+
+ // Calculate the ratio of current to expected size
+ double sizeRatio = currentSize / expectedSize;
+
+ // If the font is significantly larger than expected (over-scaled), downscale it
+ if (sizeRatio > OVERSCALE_THRESHOLD) {
+ // Calculate target size: use the base size if reasonable, otherwise use expected size
+ float targetSize;
+ if (estimatedBaseSize >= MINIMUM_FONT_SIZE && estimatedBaseSize <= MAXIMUM_REASONABLE_FONT_SIZE) {
+ // Use the calculated base size if it's reasonable
+ targetSize = estimatedBaseSize;
+ } else {
+ // Otherwise, use the expected size for the current DPI
+ targetSize = expectedSize;
+ }
+
+ // Ensure target is within reasonable bounds
+ targetSize = Math.max(MINIMUM_FONT_SIZE, Math.min(targetSize, MAXIMUM_REASONABLE_FONT_SIZE));
+
+ // Only apply if the reduction is significant enough to matter (>10% change)
+ if (Math.abs(targetSize - currentSize) / currentSize > 0.1) {
+ return font.deriveFont(targetSize);
+ }
+ }
+
+ return font;
+ }
+
+ /**
+ * Scales a font size value to match the DPI scaling of the display.
+ *
+ * @param fontSize The base font size to scale
+ * @return The scaled font size if high DPI is detected, otherwise the original size
+ */
+ public static float scaleFontSizeForDPI(float fontSize) {
+ double scaleFactor = getDpiScaleFactor();
+ if (scaleFactor > 1.0) {
+ return (float) (fontSize * scaleFactor);
+ }
+ return fontSize;
+ }
+
+ /**
+ * Scales an integer font size value to match the DPI scaling of the display.
+ *
+ * @param fontSize The base font size to scale
+ * @return The scaled font size if high DPI is detected, otherwise the original size
+ */
+ public static int scaleFontSizeForDPI(int fontSize) {
+ return Math.round(scaleFontSizeForDPI((float) fontSize));
+ }
+
+ /**
+ * Resets the cached DPI scale factor. This is useful if the display configuration
+ * changes at runtime (e.g., moving the window to a different display).
+ */
+ public static void resetCache() {
+ dpiScaleFactor = null;
+ }
+
+ /**
+ * Checks if the current display is a high DPI display.
+ *
+ * @return true if the DPI scale factor is greater than 1.0
+ */
+ public static boolean isHighDPIDisplay() {
+ return getDpiScaleFactor() > 1.0;
+ }
+
+ /**
+ * Scales all UIManager font defaults to match the DPI scaling of the display.
+ * This applies globally to all Swing components, ensuring consistent font scaling
+ * across the entire application without needing to modify individual components.
+ *
+ * This should be called once during application initialization, after setting the
+ * Look and Feel but before creating any UI components.
+ *
+ * On Windows: Fixes HTML rendering issues where fonts don't scale properly.
+ * On Linux: May downscale fonts if they appear to be over-scaled by the system.
+ * On macOS: Does nothing (macOS handles DPI scaling correctly).
+ *
+ * The scaling is conservative - it only scales fonts that appear to need adjustment,
+ * preventing double-scaling on systems that already handle it correctly.
+ */
+ public static void scaleUIManagerFonts() {
+ String osName = System.getProperty("os.name", "").toLowerCase();
+ boolean isWindows = osName.contains("windows");
+ boolean isLinux = osName.contains("linux");
+ boolean isMac = osName.contains("mac");
+
+ // On macOS, Java/Swing handles DPI scaling perfectly, so do nothing
+ if (isMac) {
+ return;
+ }
+
+ double scaleFactor = getDpiScaleFactor();
+
+ try {
+ UIDefaults defaults = UIManager.getDefaults();
+ Enumeration