From 492aa295ba4edc67d36855d6904bb7dcebdf3d7a Mon Sep 17 00:00:00 2001 From: Matthew Messinger Date: Mon, 6 Jun 2022 17:52:51 -0400 Subject: [PATCH 1/4] Add a changelog --- CHANGES.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 CHANGES.md diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..e43a957 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,27 @@ +# Changes + +## Unreleased + +- + +## 1.3.1 + +- Implement generation of glyph_sizes.bin when using legacy unicode bitmaps + +## 1.3.0 + +- Complete rewrite to support modern Minecraft versions + - Legacy support via `pack_format: 1-3` (MC 1.6.1-1.12.2) + - Font providers via `pack_format: 4` (MC 1.13+) + - TrueType fonts via `pack-format: 5` (MC 1.15+) + - Named providers via `pack_format: 6` (MC 1.16+) +- Update Java to 17 + - Java is now bundled, so it does not need to be installed separately + - Build package using `./gradlew jpackageDist`, zip will be in + `build/distributions` +- Use gson to generate json files + +## 1.2.0 + +- Use [GNU Unifont](https://unifoundry.com/unifont/index.html) as fallback font, same as Minecraft +- Decrease font size for larger fonts \ No newline at end of file From 4a6bd11b58726123493fced326086e6b64911401 Mon Sep 17 00:00:00 2001 From: Matthew Messinger Date: Sat, 11 Jun 2022 15:35:57 -0400 Subject: [PATCH 2/4] Update readme with download location --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 15fd5d8..f96b9b7 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,13 @@ underscores, then the texture. A nonexistent font will use the JRE default Note: Every font is different, so some may look weird and require tweaking after generation, but most fonts should be fine. +## Download + +The generator can be downloaded from the [Release](https://github.com/killjoy1221/HD-Font-Generator/releases/latest) +page. Pick the file for your operating system. + +Extract the zip or tar file to run the executable. + ## Options Different options can be customized before generation. These include: From ef8e5e0c69be85e27597ecea1f10f40580746bdc Mon Sep 17 00:00:00 2001 From: Matthew Messinger Date: Sat, 11 Jun 2022 15:38:32 -0400 Subject: [PATCH 3/4] Refactor Main class to separate terminal and window functions --- build.gradle | 2 +- .../java/mnm/hdfontgen/FontGenerator.java | 211 +----------------- .../java/mnm/hdfontgen/GeneratorWindow.java | 15 +- src/main/java/mnm/hdfontgen/Main.java | 13 ++ src/main/java/mnm/hdfontgen/SwingMain.java | 35 +++ src/main/java/mnm/hdfontgen/TerminalMain.java | 195 ++++++++++++++++ .../java/mnm/hdfontgen/pack/PackSettings.java | 16 +- 7 files changed, 265 insertions(+), 222 deletions(-) create mode 100644 src/main/java/mnm/hdfontgen/Main.java create mode 100644 src/main/java/mnm/hdfontgen/SwingMain.java create mode 100644 src/main/java/mnm/hdfontgen/TerminalMain.java diff --git a/build.gradle b/build.gradle index 6ca43ea..000f89d 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ java { application { // Define the main class for the application. mainModule = 'hdfontgen' - mainClass = 'mnm.hdfontgen.FontGenerator' + mainClass = 'mnm.hdfontgen.Main' } // append the java version to the dist archives diff --git a/src/main/java/mnm/hdfontgen/FontGenerator.java b/src/main/java/mnm/hdfontgen/FontGenerator.java index c3ac07f..c6282d7 100644 --- a/src/main/java/mnm/hdfontgen/FontGenerator.java +++ b/src/main/java/mnm/hdfontgen/FontGenerator.java @@ -1,36 +1,12 @@ package mnm.hdfontgen; -import mnm.hdfontgen.pack.PackFormat; -import mnm.hdfontgen.pack.PackGenerator; -import mnm.hdfontgen.pack.PackSettings; -import mnm.hdfontgen.pack.TextureSize; -import mnm.hdfontgen.pack.provider.FontProvidersJson; - -import java.awt.*; import java.io.IOException; import java.io.UncheckedIOException; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; -public class FontGenerator implements Runnable { - - private static boolean quiet; +import mnm.hdfontgen.pack.PackGenerator; +import mnm.hdfontgen.pack.PackSettings; - @Override - public void run() { - try { - var window = GeneratorWindow.instance = new GeneratorWindow(); - window.frmHdFontGenerator.setVisible(true); - } catch (Exception e) { - e.printStackTrace(); - } - } +public class FontGenerator { public static void generate(PackSettings settings, boolean parallel) throws UncheckedIOException, IOException { PackGenerator generator = settings.createGenerator(); @@ -40,185 +16,4 @@ public static void generate(PackSettings settings, boolean parallel) throws Unch pack.writeTo(filename, parallel); Log.log("Generated font at %s", filename); } - - public static void main(String[] args) { - Log.addLogger(msg -> { - if (!quiet) { - System.out.println(msg); - } - }); - if (args.length == 0) { - // open the gui - EventQueue.invokeLater(new FontGenerator()); - } else { - try { - runCli(args); - } catch (SystemExit e) { - e.exit(); - } - } - } - - private static void runCli(String[] args) throws SystemExit { - try { - var options = parseOptions(args); - if (options.containsKey("help")) { - throw printUsage(); - } - if (options.containsKey("quiet")) { - quiet = true; - } - - var settings = parseSettings(options); - - FontGenerator.generate(settings, options.containsKey("parallel")); - } catch (IOException | UncheckedIOException e) { - throw new RuntimeException(e); - } - } - - private static PackSettings parseSettings(Map options) throws SystemExit { - var format = parsePackFormat(requireOption(options, "format")); - var builder = new PackSettings.Builder(format); - if (options.containsKey("description")) { - builder = builder.withDescription(options.get("description")); - } - var type = requireOption(options, "type"); - return (switch (type) { - case "bitmap" -> builder.bitmap(FontProvidersJson.DEFAULT_NAME, b -> b - .withFont(Font.decode(requireOption(options, "font"))) - .withSize(parseTextureSize(options.getOrDefault("size", TextureSize.x32.name()))) - .withUnicode(options.containsKey("unicode")) - ); - case "truetype" -> builder.trueType(FontProvidersJson.DEFAULT_NAME, b -> b - .withFont(Paths.get(requireOption(options, "font"))) - .withOversample(Float.parseFloat(options.getOrDefault("oversample", "1"))) - ); - default -> throw printUnsupportedOption("type", type, "bitmap", "truetype"); - }).build(); -// return builder.build(); - } - - private static String requireOption(Map options, String key) throws SystemExit { - var value = options.get(key); - if (value == null) { - throw printMissingOption(key); - } - return value; - } - - /** - * Parses long options and returns them as a map. - */ - private static Map parseOptions(String[] args) { - var map = new HashMap(); - for (var i = 0; i < args.length; i++) { - var key = args[i]; - if (!key.startsWith("--")) { - throw new IllegalArgumentException("unexpected non-option argument " + key); - } - key = key.substring(2); - String value; - if (key.contains("=")) { - int index = key.indexOf("="); - value = key.substring(index + 1); - key = key.substring(0, index); - } else { - if (i + 1 == args.length || args[i + 1].startsWith("--")) { - value = ""; - } else { - value = args[i + 1]; - } - i++; - } - map.put(key, value); - } - return map; - } - - private static PackFormat parsePackFormat(String format) throws SystemExit { - return parseEnum(PackFormat.class, format, PackFormat::getFormat) - .orElseThrow(() -> printFormats(format)); - } - - private static TextureSize parseTextureSize(String size) throws SystemExit { - return parseEnum(TextureSize.class, size, TextureSize::getTextureSize) - .orElseThrow(() -> printSizes(size)); - } - - private static > Optional parseEnum(Class enumClass, String value, Function func) { - for (var en : enumClass.getEnumConstants()) { - if (Objects.toString(func.apply(en)).equals(value) || en.name().equals(value)) { - return Optional.of(en); - } - } - return Optional.empty(); - } - - private static SystemExit printMissingOption(String option) { - return new SystemExit(2, - "Option --" + option + " is missing and is required." - ); - } - - private static SystemExit printUnsupportedOption(String option, String value, String... values) { - var supported = String.join(", ", values); - return new SystemExit(2, - value + " is not a supported " + option + ".", - "Supported values are: " + supported - ); - } - - private static SystemExit printFormats(String format) { - var supported = Arrays.stream(PackFormat.values()) - .map(PackFormat::getFormat) - .map(Objects::toString) - .toArray(String[]::new); - return printUnsupportedOption("pack format", format, supported); - } - - private static SystemExit printSizes(String size) { - var supported = Arrays.stream(TextureSize.values()) - .map(TextureSize::getTextureSize) - .map(Objects::toString) - .toArray(String[]::new); - return printUnsupportedOption("texture size", size, supported); - } - - private static SystemExit printUsage() { - return new SystemExit(0, - "Command Line Usage", - "Options:", - " --type The type of font to generate (bitmap, truetype)", - " --format The pack format to use", - " --description The description to use for the pack", - " --quiet Disable log output", - " --help Displays this help text and exits", - "Bitmap Options:", - " --font The name of the font registered in the system to use", - " --size The size of font to use " + Arrays.stream(TextureSize.values()).map(Object::toString).collect(Collectors.joining(", ", "(", ")")), - " --unicode Generates unicode pages", - "TrueType Options:", - " --font The file path to a ttf or otf file", - " --oversample A decimal number specifying the resolution multiplier of the font" - ); - } - - private static class SystemExit extends RuntimeException { - private final int code; - - SystemExit(int code, String message) { - super(message); - this.code = code; - } - - SystemExit(int code, String... message) { - this(code, String.join("\n", message)); - } - - void exit() { - System.out.println(this.getMessage()); - System.exit(this.code); - } - } } \ No newline at end of file diff --git a/src/main/java/mnm/hdfontgen/GeneratorWindow.java b/src/main/java/mnm/hdfontgen/GeneratorWindow.java index 9a49623..c03664e 100644 --- a/src/main/java/mnm/hdfontgen/GeneratorWindow.java +++ b/src/main/java/mnm/hdfontgen/GeneratorWindow.java @@ -1,16 +1,17 @@ package mnm.hdfontgen; -import mnm.hdfontgen.pack.PackSettings; -import mnm.hdfontgen.pack.PackFormat; -import mnm.hdfontgen.pack.TextureSize; -import mnm.hdfontgen.pack.provider.FontProvidersJson; +import java.awt.*; +import java.io.IOException; +import java.io.UncheckedIOException; import javax.swing.*; import javax.swing.border.BevelBorder; import javax.swing.border.TitledBorder; -import java.awt.*; -import java.io.IOException; -import java.io.UncheckedIOException; + +import mnm.hdfontgen.pack.PackFormat; +import mnm.hdfontgen.pack.PackSettings; +import mnm.hdfontgen.pack.TextureSize; +import mnm.hdfontgen.pack.provider.FontProvidersJson; /* * Mostly generated using Window Builder Pro diff --git a/src/main/java/mnm/hdfontgen/Main.java b/src/main/java/mnm/hdfontgen/Main.java new file mode 100644 index 0000000..ac1d322 --- /dev/null +++ b/src/main/java/mnm/hdfontgen/Main.java @@ -0,0 +1,13 @@ +package mnm.hdfontgen; + +public class Main { + + public static void main(String[] args) { + if (args.length == 0) { + // open the gui + SwingMain.main(); + } else { + TerminalMain.main(args); + } + } +} diff --git a/src/main/java/mnm/hdfontgen/SwingMain.java b/src/main/java/mnm/hdfontgen/SwingMain.java new file mode 100644 index 0000000..91025e7 --- /dev/null +++ b/src/main/java/mnm/hdfontgen/SwingMain.java @@ -0,0 +1,35 @@ +package mnm.hdfontgen; + +import java.awt.*; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.swing.*; + +public class SwingMain implements Runnable { + + @Override + public void run() { + try { + var window = GeneratorWindow.instance = new GeneratorWindow(); + window.frmHdFontGenerator.setVisible(true); + } catch (HeadlessException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + + var sw = new StringWriter(); + var pw = new PrintWriter(sw); + e.printStackTrace(pw); + JOptionPane.showMessageDialog(null, sw.toString(), "Error!", JOptionPane.ERROR_MESSAGE); + } + } + + public static void main(String[] args) { + main(); + } + + public static void main() { + EventQueue.invokeLater(new SwingMain()); + } +} diff --git a/src/main/java/mnm/hdfontgen/TerminalMain.java b/src/main/java/mnm/hdfontgen/TerminalMain.java new file mode 100644 index 0000000..d71c85e --- /dev/null +++ b/src/main/java/mnm/hdfontgen/TerminalMain.java @@ -0,0 +1,195 @@ +package mnm.hdfontgen; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +import mnm.hdfontgen.pack.PackFormat; +import mnm.hdfontgen.pack.PackSettings; +import mnm.hdfontgen.pack.TextureSize; +import mnm.hdfontgen.pack.provider.FontProvidersJson; + +public class TerminalMain { + + private static boolean quiet; + + static { + Log.addLogger(msg -> { + if (!quiet) { + System.out.println(msg); + } + }); + } + + public static void main(String[] args) throws SystemExit { + try { + var options = parseOptions(args); + if (options.containsKey("help")) { + throw printUsage(); + } + if (options.containsKey("quiet")) { + quiet = true; + } + + var settings = parseSettings(options); + + FontGenerator.generate(settings, options.containsKey("parallel")); + } catch (IOException | UncheckedIOException e) { + throw new RuntimeException(e); + } catch (SystemExit e) { + e.exit(); + } + } + + private static PackSettings parseSettings(Map options) throws SystemExit { + var format = parsePackFormat(requireOption(options, "format")); + var builder = new PackSettings.Builder(format); + if (options.containsKey("description")) { + builder = builder.withDescription(options.get("description")); + } + var type = requireOption(options, "type"); + return (switch (type) { + case "bitmap" -> builder.bitmap(FontProvidersJson.DEFAULT_NAME, b -> b + .withFont(requireOption(options, "font")) + .withSize(parseTextureSize(options.getOrDefault("size", TextureSize.x32.name()))) + .withUnicode(options.containsKey("unicode")) + ); + case "truetype" -> builder.trueType(FontProvidersJson.DEFAULT_NAME, b -> b + .withFont(Paths.get(requireOption(options, "font"))) + .withOversample(Float.parseFloat(options.getOrDefault("oversample", "1"))) + ); + default -> throw printUnsupportedOption("type", type, "bitmap", "truetype"); + }).build(); +// return builder.build(); + } + + private static String requireOption(Map options, String key) throws SystemExit { + var value = options.get(key); + if (value == null) { + throw printMissingOption(key); + } + return value; + } + + /** + * Parses long options and returns them as a map. + */ + private static Map parseOptions(String[] args) { + var map = new HashMap(); + for (var i = 0; i < args.length; i++) { + var key = args[i]; + if (!key.startsWith("--")) { + throw new IllegalArgumentException("unexpected non-option argument " + key); + } + key = key.substring(2); + String value; + if (key.contains("=")) { + int index = key.indexOf("="); + value = key.substring(index + 1); + key = key.substring(0, index); + } else { + if (i + 1 == args.length || args[i + 1].startsWith("--")) { + value = ""; + } else { + value = args[i + 1]; + } + i++; + } + map.put(key, value); + } + return map; + } + + private static PackFormat parsePackFormat(String format) throws SystemExit { + return parseEnum(PackFormat.class, format, PackFormat::getFormat) + .orElseThrow(() -> printFormats(format)); + } + + private static TextureSize parseTextureSize(String size) throws SystemExit { + return parseEnum(TextureSize.class, size, TextureSize::getTextureSize) + .orElseThrow(() -> printSizes(size)); + } + + private static > Optional parseEnum(Class enumClass, String value, Function func) { + for (var en : enumClass.getEnumConstants()) { + if (Objects.toString(func.apply(en)).equals(value) || en.name().equals(value)) { + return Optional.of(en); + } + } + return Optional.empty(); + } + + private static SystemExit printMissingOption(String option) { + return new SystemExit(2, + "Option --" + option + " is missing and is required." + ); + } + + private static SystemExit printUnsupportedOption(String option, String value, String... values) { + var supported = String.join(", ", values); + return new SystemExit(2, + value + " is not a supported " + option + ".", + "Supported values are: " + supported + ); + } + + private static SystemExit printFormats(String format) { + var supported = Arrays.stream(PackFormat.values()) + .map(PackFormat::getFormat) + .map(Objects::toString) + .toArray(String[]::new); + return printUnsupportedOption("pack format", format, supported); + } + + private static SystemExit printSizes(String size) { + var supported = Arrays.stream(TextureSize.values()) + .map(TextureSize::getTextureSize) + .map(Objects::toString) + .toArray(String[]::new); + return printUnsupportedOption("texture size", size, supported); + } + + private static SystemExit printUsage() { + return new SystemExit(0, + "Command Line Usage", + "Options:", + " --type The type of font to generate (bitmap, truetype)", + " --format The pack format to use", + " --description The description to use for the pack", + " --quiet Disable log output", + " --help Displays this help text and exits", + "Bitmap Options:", + " --font The name of the font registered in the system to use", + " --size The size of font to use " + Arrays.stream(TextureSize.values()).map(Object::toString).collect(Collectors.joining(", ", "(", ")")), + " --unicode Generates unicode pages", + "TrueType Options:", + " --font The file path to a ttf or otf file", + " --oversample A decimal number specifying the resolution multiplier of the font" + ); + } + + private static class SystemExit extends RuntimeException { + private final int code; + + SystemExit(int code, String message) { + super(message); + this.code = code; + } + + SystemExit(int code, String... message) { + this(code, String.join("\n", message)); + } + + void exit() { + System.out.println(this.getMessage()); + System.exit(this.code); + } + } +} diff --git a/src/main/java/mnm/hdfontgen/pack/PackSettings.java b/src/main/java/mnm/hdfontgen/pack/PackSettings.java index 792c805..3fd1f78 100644 --- a/src/main/java/mnm/hdfontgen/pack/PackSettings.java +++ b/src/main/java/mnm/hdfontgen/pack/PackSettings.java @@ -1,11 +1,5 @@ package mnm.hdfontgen.pack; -import mnm.hdfontgen.pack.generator.FontProviderFontGenerator; -import mnm.hdfontgen.pack.generator.LegacyFontGenerator; -import mnm.hdfontgen.pack.provider.FontProvider; -import mnm.hdfontgen.pack.provider.FontProvidersJson; -import mnm.hdfontgen.pack.provider.StandardFontProviders; - import java.awt.*; import java.nio.file.Path; import java.util.ArrayList; @@ -16,6 +10,12 @@ import java.util.function.Predicate; import java.util.function.UnaryOperator; +import mnm.hdfontgen.pack.generator.FontProviderFontGenerator; +import mnm.hdfontgen.pack.generator.LegacyFontGenerator; +import mnm.hdfontgen.pack.provider.FontProvider; +import mnm.hdfontgen.pack.provider.FontProvidersJson; +import mnm.hdfontgen.pack.provider.StandardFontProviders; + public class PackSettings { public final PackFormat format; public final String description; @@ -164,6 +164,10 @@ private String makeDescription() { return String.format("%s %s%s for Minecraft %s", fontName, size, withUnicode, versions); } + public BitmapBuilder withFont(String font) { + return withFont(Font.decode(font)); + } + public BitmapBuilder withFont(Font font) { this.font = Objects.requireNonNull(font); return this; From df4e42f21962d5cdf97617e76e0d954e61bae583 Mon Sep 17 00:00:00 2001 From: Matthew Messinger Date: Wed, 17 Jan 2024 21:50:56 -0500 Subject: [PATCH 4/4] Update readme with development notes --- README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f96b9b7..41cb7ed 100644 --- a/README.md +++ b/README.md @@ -35,4 +35,17 @@ Different options can be customized before generation. These include: - **Unicode**: (legacy), Generates the full extended unicode, which consists of 256 pages, each with 256 characters. Fallback font uses the [gnu uniform font](http://unifoundry.com/unifont/). -- **Parallel**: Uses multiple threads to generate font bitmaps in parallel. \ No newline at end of file +- **Parallel**: Uses multiple threads to generate font bitmaps in parallel. + +## Development + +Note: The project uses JDK 17. + +### Gradle Tasks + +The following Gradle tasks can be used to run or build the project. + +- `./gradlew run` - Runs the project with gradle +- `./gradlew build` - Build the project and package dependencies with a launch script. This is useful if you have the JDK installed already, though you have to extract it manually. +- `./gradlew jPackageDist` - Create a platform-specific archive which bundles the JRE. The unarchived build files are located in `build/jpackage`. +