From 73f55565955e1dcdfaf095bb8c06db4f5916b673 Mon Sep 17 00:00:00 2001 From: danyadev Date: Sun, 6 Sep 2026 07:09:41 +0300 Subject: [PATCH] support custom fonts on macos (#2066) --- .editorconfig | 6 + .../angelica/client/font/FontStrategist.java | 107 +++++++++++------- 2 files changed, 74 insertions(+), 39 deletions(-) diff --git a/.editorconfig b/.editorconfig index 4ffbd9082..4052d9234 100644 --- a/.editorconfig +++ b/.editorconfig @@ -20,3 +20,9 @@ indent_size = 2 [*.lang] trim_trailing_whitespace = false + +[*.java] +ij_java_use_single_class_imports = true +ij_java_class_count_to_use_import_on_demand = 999 +ij_java_names_count_to_use_import_on_demand = 999 +ij_java_packages_to_use_import_on_demand = empty diff --git a/src/main/java/com/gtnewhorizons/angelica/client/font/FontStrategist.java b/src/main/java/com/gtnewhorizons/angelica/client/font/FontStrategist.java index 00bbef2b8..183d4a593 100644 --- a/src/main/java/com/gtnewhorizons/angelica/client/font/FontStrategist.java +++ b/src/main/java/com/gtnewhorizons/angelica/client/font/FontStrategist.java @@ -4,6 +4,7 @@ import com.google.common.collect.HashMultiset; import com.gtnewhorizons.angelica.config.FontConfig; import com.gtnewhorizons.angelica.mixins.interfaces.ResourceAccessor; import cpw.mods.fml.client.SplashProgress; +import cpw.mods.fml.common.versioning.DefaultArtifactVersion; import lombok.Getter; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; @@ -12,13 +13,15 @@ import net.minecraft.client.resources.SimpleReloadableResourceManager; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import cpw.mods.fml.common.Loader; - import java.awt.Font; import java.awt.FontFormatException; import java.awt.GraphicsEnvironment; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; @@ -29,42 +32,46 @@ public class FontStrategist { @Getter private static final Font[] availableFonts; public static final Logger LOGGER = LogManager.getLogger("Angelica"); + private static final boolean isSafeToUseAwtEnvironmentData = checkIsSafeToUseAwtEnvironmentData(); static { HashMap fontSet = new HashMap<>(); - if (GraphicsEnvironment.isHeadless()) { - LOGGER.warn("GraphicsEnvironment.isHeadless() returned true! Only bundled fonts will be available. This is likely a MacOS issue."); + // get available fonts without duplicates (250 copies of dialog.plain need not apply) + final Font[] availableFontsDirty; + final HashMultiset duplicates = HashMultiset.create(); // for debugging + + if (isSafeToUseAwtEnvironmentData) { + availableFontsDirty = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); } else { - // get available fonts without duplicates (250 copies of dialog.plain need not apply) - Font[] availableFontsDirty = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); - HashMultiset duplicates = HashMultiset.create(); // for debugging - - for (Font font : availableFontsDirty) { - String fontName = font.getFontName(); - if (fontSet.containsKey(fontName)) { - duplicates.add(fontName); - } else { - fontSet.put(fontName, font); - } - } - - if (!duplicates.isEmpty()) { - StringBuilder sb = new StringBuilder(duplicates.size() + " duplicate font(s) found in the list reported by Java: "); - for (Iterator iter = duplicates.stream().distinct().iterator(); iter.hasNext(); ) { - String dupe = iter.next(); - sb.append(duplicates.count(dupe)).append("x ").append(dupe); - if (iter.hasNext()) { - sb.append(", "); - } - } - sb.append(". Some fonts may be missing from the font selection menu."); - LOGGER.warn(sb.toString()); - } - - LOGGER.info("Got {} fonts from GraphicsEnvironment ({} after deduplication)", availableFontsDirty.length, fontSet.size()); + LOGGER.warn("System font enumeration is disabled because AWT API is unsafe in this environment. Update lwjgl3ify!"); + availableFontsDirty = new Font[0]; } + for (Font font : availableFontsDirty) { + String fontName = font.getFontName(); + if (fontSet.containsKey(fontName)) { + duplicates.add(fontName); + } else { + fontSet.put(fontName, font); + } + } + + if (!duplicates.isEmpty()) { + StringBuilder sb = new StringBuilder(duplicates.size() + " duplicate font(s) found in the list reported by Java: "); + for (Iterator iter = duplicates.stream().distinct().iterator(); iter.hasNext(); ) { + String dupe = iter.next(); + sb.append(duplicates.count(dupe)).append("x ").append(dupe); + if (iter.hasNext()) { + sb.append(", "); + } + } + sb.append(". Some fonts may be missing from the font selection menu."); + LOGGER.warn(sb.toString()); + } + + LOGGER.info("Got {} fonts from GraphicsEnvironment ({} after deduplication)", availableFontsDirty.length, fontSet.size()); + loadBundledFonts(fontSet); availableFonts = fontSet.values().stream().sorted(Comparator.comparing(Font::getFontName)).toArray(Font[]::new); @@ -83,17 +90,39 @@ public class FontStrategist { ((SimpleReloadableResourceManager) mc.getResourceManager()).reloadResourcePack(fontResourcePack); } + private static boolean checkIsSafeToUseAwtEnvironmentData() { + if (!GraphicsEnvironment.isHeadless()) return true; + + try (InputStream stream = ClassLoader.getSystemResourceAsStream( + "META-INF/lwjgl3ify-forgePatches-version.txt")) { + + if (stream == null) { + LOGGER.warn("Failed to identify lwjgl3ify version"); + return false; + } + + String version; + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(stream, StandardCharsets.UTF_8))) { + version = reader.readLine(); + } + + // See https://github.com/GTNewHorizons/RetroFuturaBootstrap/commit/6d39c56ba0f1496b5599a3660d1578df3b28ff17 + // This change corresponds to RFB 1.0.14, used since lwjgl3ify 3.0.8 + return new DefaultArtifactVersion(version) + .compareTo(new DefaultArtifactVersion("3.0.8")) >= 0; + } catch (IOException e) { + LOGGER.warn("Failed to identify lwjgl3ify version", e); + return false; + } + } + // Load .ttf/.otf shipped with the pack so customFontName* can point at a font that isn't installed // on the system. fontfiles/ is SmoothFont's folder, kept so packs built for it work unchanged. private static void loadBundledFonts(HashMap fontSet) { - File configDir = Loader.instance().getConfigDir(); - if (configDir == null) { - LOGGER.warn("Loader.instance().getConfigDir() returned null. Bundled fonts will not be loaded."); - return; - } - File parent = configDir.getParentFile(); - File[] fontDirs = { new File(parent, "fontfiles"), new File(configDir, "angelica/fonts") }; - GraphicsEnvironment ge = GraphicsEnvironment.isHeadless() ? null : GraphicsEnvironment.getLocalGraphicsEnvironment(); + File gameDir = Minecraft.getMinecraft().mcDataDir; + File[] fontDirs = { new File(gameDir, "fontfiles"), new File(gameDir, "config/angelica/fonts") }; + GraphicsEnvironment ge = !isSafeToUseAwtEnvironmentData ? null : GraphicsEnvironment.getLocalGraphicsEnvironment(); int loaded = 0; for (File dir : fontDirs) { File[] files = dir.listFiles((d, name) -> name.toLowerCase().endsWith(".ttf") || name.toLowerCase().endsWith(".otf"));