import os import urllib.request def download_file(url: str, filename: str): if not os.path.exists(filename): print(f"Downloading {filename} from {url}") urllib.request.urlretrieve(url, filename) else: print(f"{filename} already exists, skipping download.") def generate(bpp, size, font_file: str, symbols: list, output: str): output_file_name = f"{output}_{size}.c" output_path = os.path.join("..", "source-fonts", output_file_name) print(f"Generating {output_file_name}") cmd = "lv_font_conv --no-compress --no-prefilter --bpp {} --size {} --font {} -r {} --format lvgl -o {} --force-fast-kern-format".format(bpp, size, font_file, ",".join(symbols), output_path) ret = os.system(cmd) if ret != 0: raise RuntimeError(f"Font generation failed for {output_file_name} (exit code {ret})") def read_code_points_map(file_path: str): codepoints = {} with open(file_path, 'r') as f: for line in f: parts = line.strip().split() if len(parts) >= 2: name, cp = parts[0].lower(), parts[1] codepoints[name] = cp if len(codepoints) == 0: raise ValueError(f"Code points map is empty or wasn't found at {file_path}") return codepoints def get_code_points(codepoints, names: list) -> list: result = [] for name in names: safe_name = name.lower() if not safe_name in codepoints: raise ValueError(f"Code point '{safe_name}' not found in map") result.append(f"0x{codepoints[safe_name].upper()}") return result def generate_icon_fonts(font_file, font_sizes, symbols, output): for size in font_sizes: generate(2, size, font_file, symbols, output) def generate_icon_names(codepoint_map: dict, codepoint_names: list, variable_name: str): filename = f"lvgl_icon_{variable_name}.h" print(f"Generating {filename}") output_path = os.path.join("..", "include", "tactility", filename) with open(output_path, 'w') as f: f.write("#pragma once\n\n") for name in codepoint_names: safe_name = name.lower() if safe_name in codepoint_map: hex_val = codepoint_map[safe_name] # Convert hex to int val = int(hex_val, 16) # Convert to UTF-8 escaped string utf8_bytes = chr(val).encode('utf-8') escaped_str = "".join(f"\\x{b:02X}" for b in utf8_bytes) f.write(f'#define LVGL_ICON_{variable_name.upper()}_{safe_name.upper()} "{escaped_str}"\n') else: print(f"Warning: {safe_name} not found in codepoint map") # --------------- Symbol Fonts --------------- # Get more from https://fonts.google.com/icons?icon.set=Material+Symbols&icon.style=Rounded shared_symbol_code_point_names = [ "add", "apps", "area_chart", # System Info "app_registration", # App Settings app "calendar_month", "cable", "circle", "close", "cloud", # Web server "check", "delete", "devices", # Developer app "display_settings", # Display Settings app "edit_note", "electric_bolt", # Power (settings) app "folder", "deployed_code", # 3D cube "download", "forum", # Chat app "gamepad", "help", # Diceware help "hub", # App Hub "image", # Screenshot app "keyboard_arrow_up", "lightbulb", "language", # Globe "lists", # Chat app toolbar "mail", "menu", "mop", "more_vert", "music_note", "note_add", "power_settings_new", # Power off for T-Lora Pager "refresh", # e.g. App Hub reload button "search", "settings", "toolbar", # Apps without custom icon "navigation", # GPS (settings) app "keyboard_alt", # Keyboard (settings) app "usb", # Power (settings) app "wifi", # WiFi (settings) app ] # Get more from https://fonts.google.com/icons?icon.set=Material+Symbols&icon.style=Rounded statusbar_symbol_code_point_names = [ # Location tracking "location_on", # Development server "cloud", # Low on available memory "memory", # SD Card "sd_card", "sd_card_alert", # Wi-Fi "signal_wifi_0_bar", "network_wifi_1_bar", "network_wifi_2_bar", "network_wifi_3_bar", "signal_wifi_4_bar", "signal_wifi_off", "signal_wifi_bad", # Battery "battery_android_frame_1", "battery_android_frame_2", "battery_android_frame_3", "battery_android_frame_4", "battery_android_frame_5", "battery_android_frame_6", "battery_android_frame_full", "battery_android_frame_bolt" ] # Get more from https://fonts.google.com/icons?icon.set=Material+Symbols&icon.style=Rounded launcher_symbol_code_point_names = [ "apps", "folder", "settings" ] shared_symbol_sizes = [ 12, 16, # T-Deck reference, with montserrat 14 in lv_list as icon with text 20, 24, 32 ] statusbar_symbol_sizes = [ 12, 16, # T-Deck reference 20, 30 ] launcher_symbol_sizes = [ 30, 36, # T-Deck reference 42, 48, 64, 72 ] # Resolve file path relative to this script so it can be executed from any CWD base_dir = os.path.dirname(__file__) if base_dir: os.chdir(base_dir) codepoints_url = "https://github.com/google/material-design-icons/raw/refs/heads/master/variablefont/MaterialSymbolsRounded%5BFILL,GRAD,opsz,wght%5D.codepoints" ttf_url = "https://github.com/google/material-design-icons/raw/refs/heads/master/variablefont/MaterialSymbolsRounded%5BFILL,GRAD,opsz,wght%5D.ttf" codepoints_filename = "MaterialSymbolsRounded.codepoints" ttf_filename = "MaterialSymbolsRounded.ttf" download_file(codepoints_url, codepoints_filename) download_file(ttf_url, ttf_filename) codepoints_map_path = codepoints_filename codepoints_map = read_code_points_map(codepoints_map_path) # Shared symbols shared_symbol_code_points = get_code_points(codepoints_map, shared_symbol_code_point_names) generate_icon_fonts(ttf_filename, shared_symbol_sizes, shared_symbol_code_points, "material_symbols_shared") generate_icon_names(codepoints_map, shared_symbol_code_point_names, "shared") # Statusbar symbols statusbar_symbol_code_points = get_code_points(codepoints_map, statusbar_symbol_code_point_names) generate_icon_fonts(ttf_filename, statusbar_symbol_sizes, statusbar_symbol_code_points, "material_symbols_statusbar") generate_icon_names(codepoints_map, statusbar_symbol_code_point_names, "statusbar") # Launcher symbols launcher_symbol_code_points = get_code_points(codepoints_map, launcher_symbol_code_point_names) generate_icon_fonts(ttf_filename, launcher_symbol_sizes, launcher_symbol_code_points, "material_symbols_launcher") generate_icon_names(codepoints_map, launcher_symbol_code_point_names, "launcher")