diff --git a/src/main/java/cc/fros/features/modules/client/ClickGuiModule.java b/src/main/java/cc/fros/features/modules/client/ClickGuiModule.java index 8ad0d18..21029e0 100644 --- a/src/main/java/cc/fros/features/modules/client/ClickGuiModule.java +++ b/src/main/java/cc/fros/features/modules/client/ClickGuiModule.java @@ -16,7 +16,7 @@ import static cc.fros.features.commands.MessageSignatures.GENERAL; public class ClickGuiModule extends Module { private static ClickGuiModule INSTANCE; - public final Setting font = str("Font", ""); + public final Setting font = str("Font", "SansSerif"); public final Setting fontSize = num("FontSize", 10, 6, 24); public final Setting prefix = str("Prefix", "."); public final Setting color = color("Color", 0, 0, 255, 180); diff --git a/src/main/java/cc/fros/util/render/FrosFont.java b/src/main/java/cc/fros/util/render/FrosFont.java index 9802bb7..0e0b574 100644 --- a/src/main/java/cc/fros/util/render/FrosFont.java +++ b/src/main/java/cc/fros/util/render/FrosFont.java @@ -50,6 +50,14 @@ public final class FrosFont { }; private static final char[] HEX = "0123456789abcdef".toCharArray(); + // java guarantees these logical fonts on every os + private static final Map LOGICAL_FONTS = Map.of( + "sansserif", "SansSerif", + "serif", "Serif", + "monospaced", "Monospaced"); + + private static String[] installedFamilies; + private static final Map GLYPHS = new LinkedHashMap<>(128, 0.75f, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { @@ -159,18 +167,19 @@ public final class FrosFont { if (name.isBlank()) return; try { Path file = resolveFontFile(name); - if (file == null) { - if (verbose) LOGGER.warn("Font '{}' not found in {}", name, FONT_DIR); + if (file != null) { + applyFont(java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, file.toFile()), + size, "Loaded font " + file.getFileName(), verbose); return; } - awtFont = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, file.toFile()) - .deriveFont(java.awt.Font.PLAIN, size); - BufferedImage probe = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); - Graphics2D graphics = probe.createGraphics(); - graphics.setFont(awtFont); - metrics = graphics.getFontMetrics(); - graphics.dispose(); - if (verbose) LOGGER.info("Loaded font {} ({}pt)", file.getFileName(), size); + + java.awt.Font installed = installedFont(name); + if (installed != null) { + applyFont(installed, size, "Using system font '" + name + "'", verbose); + return; + } + + if (verbose) LOGGER.warn("Font '{}' not found in {}", name, FONT_DIR); } catch (Exception e) { LOGGER.error("Failed to load font '{}'", name, e); awtFont = null; @@ -178,6 +187,35 @@ public final class FrosFont { } } + private static void applyFont(java.awt.Font font, float size, String source, boolean verbose) { + awtFont = font.deriveFont(java.awt.Font.PLAIN, size); + BufferedImage probe = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = probe.createGraphics(); + graphics.setFont(awtFont); + metrics = graphics.getFontMetrics(); + graphics.dispose(); + if (verbose) LOGGER.info("{} ({}pt)", source, size); + } + + /** + * Resolves java's logical fonts (guaranteed on every os) and any font + * family already installed on the system, without needing a file. + */ + private static java.awt.Font installedFont(String name) { + String logical = LOGICAL_FONTS.get(name.toLowerCase(Locale.ROOT)); + if (logical != null) return new java.awt.Font(logical, java.awt.Font.PLAIN, 1); + + if (installedFamilies == null) { + installedFamilies = java.awt.GraphicsEnvironment + .getLocalGraphicsEnvironment() + .getAvailableFontFamilyNames(); + } + for (String family : installedFamilies) { + if (family.equalsIgnoreCase(name)) return new java.awt.Font(family, java.awt.Font.PLAIN, 1); + } + return null; + } + private static Path resolveFontFile(String name) { try { Files.createDirectories(FONT_DIR);