import os from tkinter import filedialog import json import base64 import numpy as np def get_choice(text, choices): line = "=" * len(text) print(line) print(text) print(line) choices_len = len(choices) for i, choice in enumerate(choices): print(f"{i+1}. {choice}") while True: try: choice = int(input(f"> ")) except ValueError: print("\nInvalid input! Input must be a number.") continue if choice < 1 or choice > choices_len: print(f"\nInvalid input! Choose number from the list.") continue return choice - 1 def get_int_input(text, min_val=None, max_val=None): while True: try: value = int(input(text)) except ValueError: print("\nInvalid input! Input must be a number.") continue if (min_val is not None and value < min_val) or (max_val is not None and value > max_val): print(f"\nInvalid input! Input must be between {min_val} and {max_val}.") continue return value def key_tips(): print("\n======== CONTROLS ========") print("Scroll - Select character") print("Mouse click/drag - Draw") print("Delete - Remove character") print("G - Toggle grid") print("E - Export font") print("P - Reprint this controls info") print("\nBlank characters are automatically removed.") #print("Type any character into the console and press enter at any time to jump to it.") print("============================\n") def cli_main(): while True: choices = ["Create new font", "Open font project"] # if last_path_cache.txt exists, read it try: with open("last_path_cache.txt", "r") as file: last_path = file.read() choices.append(f"Open recent project ({os.path.basename(last_path)})") except FileNotFoundError: last_path = None choice = get_choice( "What do you want to do?" + (" No recently opened project found." if last_path is None else ""), choices ) #choice = 2 # create new font project if choice == 0: print("\n" + "=" * 30) project_data = { "char_width": get_int_input("Enter character width: ", min_val=2), "char_height": get_int_input("Enter character height: ", min_val=2), "chars": {} } # show file save dialog file_path = filedialog.asksaveasfilename( title="Save font project", filetypes=[("Font project (json)", "*.fontproj")], defaultextension=".fontproj" ) if file_path == "": print("\nCanceled.\n") continue # save project data to file with open(file_path, "w") as f: json.dump(project_data, f, indent=4) # save last path to cache with open("last_path_cache.txt", "w") as f: f.write(file_path) # open existing font project elif choice == 1: pass # open last project elif choice == 2: file_path = last_path try: with open(file_path, "r") as f: project_data = json.load(f) except FileNotFoundError: print("\nCouldn't open last project. File not found.\n") continue # process project data if opened existing project if choice != 0: # reverse the packed characters unpacked_chars = {} for key, value in project_data["chars"].items(): # decode from base64 decoded_data = base64.b64decode(value.encode("utf-8")) # unpackbits unpacked_data = np.unpackbits(np.frombuffer(decoded_data, dtype=np.uint8)) # remove padding unpacked_data = unpacked_data[:project_data["char_width"] * project_data["char_height"]] # reshape into original shape unpacked_data = unpacked_data.reshape(project_data["char_width"], project_data["char_height"]) # store unpacked character unpacked_chars[key] = unpacked_data.astype(bool) project_data["chars"] = unpacked_chars print("\n" + "=" * 30) print(f"Font resolution: {project_data['char_width']}x{project_data['char_height']}") key_tips() return project_data, file_path