diff --git a/README.md b/README.md index ea12ca3..52032aa 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This project aims to be 100% faithful to the original Minecraft font by providin 1. Clone this repo 2. Run `uv run src/minecraft_seven/__init__.py` 3. Open [Pixel Font Converter](https://yal.cc/r/20/pixelfont/) -4. Click `Menu` in the top right and select `Batch process` +4. Click `Menu` on the upper left and select `Batch process` 5. Navigate to the `out` folder in this repo and select both the `png` and `json` file. (by holding Ctrl) 6. Install the ttf in the downloaded zip! diff --git a/resources/1.21.toml b/resources/1.21.toml index 1d87480..50d651f 100644 --- a/resources/1.21.toml +++ b/resources/1.21.toml @@ -1,7 +1,4 @@ -[tileset] -tile_width = 9 -tile_height = 12 -tile_baseline = 10 +[output] tileset_width = 21717 [providers."minecraft:font/ascii.png"] diff --git a/src/minecraft_seven/__init__.py b/src/minecraft_seven/__init__.py index 9db34bb..b8479b0 100644 --- a/src/minecraft_seven/__init__.py +++ b/src/minecraft_seven/__init__.py @@ -2,9 +2,11 @@ import os from zipfile import ZipFile from xdialog import open_file -from minecraft_seven.builder import build_as_pixel_font_converter_batch +from minecraft_seven.builder import build_font_assets import json +from minecraft_seven.pixel_font_exporter import export_to_pixel_font_converter_batch + MINECRAFT_VERSION = "1.21" @@ -36,11 +38,14 @@ def check_minecraft_version(jar_path: str): def main() -> None: + print("Getting jar file...") jar_path = get_jar_path() check_minecraft_version(jar_path) - build_as_pixel_font_converter_batch(jar_path, MINECRAFT_VERSION) - return + tileset, glyphs, dimensions = build_font_assets(jar_path, MINECRAFT_VERSION) + + print("Exporting...") + export_to_pixel_font_converter_batch(tileset, glyphs, dimensions) if __name__ == "__main__": diff --git a/src/minecraft_seven/builder.py b/src/minecraft_seven/builder.py index 3c82425..a4b9f97 100644 --- a/src/minecraft_seven/builder.py +++ b/src/minecraft_seven/builder.py @@ -5,6 +5,9 @@ from PIL import Image, ImageDraw +type MinecraftClientJar = ZipFile + + class Provider: id: str file: bytes @@ -14,39 +17,74 @@ class Provider: chars: list[str] -def get_assets(jar_path: str, tile_data: dict) -> (list[Provider], int): +class OutputDimensions: + tile_width: int + tile_height: int + tile_baseline: int + space_width: int + tileset_width: int + + +def get_space_width(jar: MinecraftClientJar): + space_data: dict = json.loads(jar.read("assets/minecraft/font/include/space.json")) + return space_data["providers"][0]["advances"][" "] + + +def get_font_data(jar: MinecraftClientJar): + return json.loads(jar.read("assets/minecraft/font/include/default.json")) + + +def get_assets( + jar_path: str, dimensions: dict +) -> tuple[list[Provider], OutputDimensions]: + out = OutputDimensions() + out.tileset_width = dimensions["output"]["tileset_width"] + with ZipFile(jar_path, "r") as jar: - default_data: dict = json.loads( - jar.read("assets/minecraft/font/include/default.json") - ) - space_data: dict = json.loads( - jar.read("assets/minecraft/font/include/space.json") - ) - space_width = space_data["providers"][0]["advances"][" "] - providers: list[dict] = default_data["providers"] + font_data = get_font_data(jar) + providers: list[dict] = font_data["providers"] new_providers: list[Provider] = [] + out.tile_width = 0 + out.tile_height = 0 + out.tile_baseline = 0 + for provider in providers: - new_provider: Provider = Provider() - texture_id: str = provider["file"] - new_provider.id = texture_id - texture_name = texture_id.replace( - "minecraft:", "assets/minecraft/textures/" + provider_dimensions = dimensions["providers"][provider["file"]] + new_provider = map_provider_json_to_class( + jar, provider, provider_dimensions ) - new_provider.file = jar.read(texture_name) - - provider_tile_data = tile_data["providers"][texture_id] - new_provider.width = provider_tile_data["width"] - new_provider.height = provider_tile_data["height"] - new_provider.chars = provider["chars"] - new_provider.baseline = provider["ascent"] + if new_provider.width > out.tile_width: + out.tile_width = new_provider.width + if new_provider.height > out.tile_height: + out.tile_height = new_provider.height + out.tile_baseline = new_provider.baseline new_providers.append(new_provider) - return new_providers, space_width + out.space_width = get_space_width(jar) + + return new_providers, out -def load_tile_data(mc_version: str) -> dict: +def map_provider_json_to_class(jar: MinecraftClientJar, provider, provider_dimensions): + new_provider: Provider = Provider() + + texture_id: str = provider["file"] + new_provider.id = texture_id + + texture_name = texture_id.replace("minecraft:", "assets/minecraft/textures/") + new_provider.file = jar.read(texture_name) + + new_provider.chars = provider["chars"] + new_provider.baseline = provider["ascent"] + new_provider.width = provider_dimensions["width"] + new_provider.height = provider_dimensions["height"] + + return new_provider + + +def load_dimensions(mc_version: str) -> dict: filename = "resources/" + mc_version + ".toml" with open(filename, "r") as file: return tomllib.loads(file.read()) @@ -60,70 +98,57 @@ def create_space_tile(width: int, height: int, space_width: int) -> Image: def build_tileset( - tileset_data: dict, providers: list[Provider], space_width: int -) -> (Image, str): - tile_width = tileset_data["tile_width"] - tile_height = tileset_data["tile_height"] - tile_baseline = tileset_data["tile_baseline"] - tileset_width = tileset_data["tileset_width"] - - tileset = Image.new("RGBA", (tileset_width, tile_height), color=(255, 0, 0, 0)) + providers: list[Provider], out: OutputDimensions +) -> tuple[Image, str]: + tileset = Image.new( + "RGBA", (out.tileset_width, out.tile_height), color=(255, 0, 0, 0) + ) glyphs: str = "" tileset_x = 0 for provider in providers: - char_height = provider.height - char_width = provider.width - char_baseline = provider.baseline - tile_height_offset = tile_baseline - char_baseline + tile_height_offset = out.tile_baseline - provider.baseline font_img = Image.open(io.BytesIO(provider.file)) font_x = font_y = 0 for chars in provider.chars: # "abcdefghij" for char in chars: # "a" - if char != "\x00": - print(char) - if char != " ": - char_img = font_img.crop( - (font_x, font_y, font_x + char_width, font_y + char_height) + if char == "\x00": + break + + if char == " ": + char_img = create_space_tile( + provider.width, provider.height, out.space_width + ) + else: + char_img = font_img.crop( + ( + font_x, + font_y, + font_x + provider.width, + font_y + provider.height, ) - else: - char_img = create_space_tile( - char_width, char_height, space_width - ) - tileset.paste(char_img, (tileset_x, tile_height_offset)) - glyphs += char + ) + tileset.paste(char_img, (tileset_x, tile_height_offset)) + glyphs += char + tileset_x += out.tile_width + font_x += provider.width - tileset_x += tile_width - font_x += char_width + # Start at 0 at next row font_x = 0 - font_y += char_height + font_y += provider.height return tileset, glyphs -def convert_to_pixel_font_converter_batch( - tileset: Image, glyphs: str, tileset_data: dict -): - with open("resources/pixel_font_converter_settings.json") as settings_file: - settings = json.load(settings_file) - - settings["glyph-width"] = tileset_data["tile_width"] - settings["glyph-height"] = tileset_data["tile_height"] - settings["glyph-baseline"] = tileset_data["tile_baseline"] - - settings["in-glyphs"] = [glyphs] +def build_font_assets( + jar_path: str, mc_version: str +) -> tuple[Image, str, OutputDimensions]: + print("Getting assets...") + dimensions = load_dimensions(mc_version) + providers, output_dimensions = get_assets(jar_path, dimensions) - with open("out/Minecraft Seven.json", "w") as settings_output_file: - json.dump(settings, settings_output_file) + print("Building tileset...") + tileset, glyphs = build_tileset(providers, output_dimensions) - tileset.save("out/Minecraft Seven.png") - - -def build_as_pixel_font_converter_batch(jar_path: str, mc_version: str): - tile_data = load_tile_data(mc_version) - providers, space_width = get_assets(jar_path, tile_data) - for provider in providers: - print(provider.id) - tileset, glyphs = build_tileset(tile_data["tileset"], providers, space_width) - convert_to_pixel_font_converter_batch(tileset, glyphs, tile_data["tileset"]) + return tileset, glyphs, output_dimensions diff --git a/src/minecraft_seven/pixel_font_exporter.py b/src/minecraft_seven/pixel_font_exporter.py new file mode 100644 index 0000000..3afe819 --- /dev/null +++ b/src/minecraft_seven/pixel_font_exporter.py @@ -0,0 +1,47 @@ +import json + +from PIL import Image + +from minecraft_seven.builder import OutputDimensions + + +def export_to_pixel_font_converter_batch( + tileset: Image, glyphs: str, out: OutputDimensions +): + with open("resources/pixel_font_converter_settings.json") as settings_file: + settings = json.load(settings_file) + + settings["glyph-width"] = out.tile_width + settings["glyph-height"] = out.tile_height + settings["glyph-baseline"] = out.tile_baseline + + settings["in-glyphs"] = [glyphs] + + with open("out/Minecraft Seven.json", "w") as settings_output_file: + json.dump(settings, settings_output_file) + + tileset.save("out/Minecraft Seven.png") + + show_done_msg() + + +def show_done_msg(): + print(f""" +Done! + +1. Go to {link("https://yal.cc/r/20/pixelfont/", "[🔗 Pixel Font Converter]")} +2. Click `Menu` on the upper left and select `Batch process` +3. Navigate to the `out` folder in this repo and select both the `png` and `json` file. (by holding `Ctrl`) +4. Install the ttf in the downloaded zip! + """) + + +def link(uri: str, label: str = None): + if label is None: + label = uri + parameters = "" + + # OSC 8 ; params ; URI ST OSC 8 ;; ST + escape_mask = "\033]8;{};{}\033\\{}\033]8;;\033\\" + + return escape_mask.format(parameters, uri, label)