import argparse
from font_api import get_table_names, get_value, load, load_table
NUMBER_OF_METADATA_ENTRIES = 8
CHAR_HEADER_LEN = 4
def reverse_bits_u8(value: int) -> int:
v = value & 0xFF
r = 0
for _ in range(8):
r = (r << 1) | (v & 1)
v >>= 1
return r
def main() -> None:
parser = argparse.ArgumentParser(description="Convert a C font file to Rust format")
parser.add_argument("input_c", help="Path to the input C file")
parser.add_argument("output_rs", help="Path to the output RS file")
args = parser.parse_args()
load(args.input_c)
table_names = get_table_names()
if not table_names:
raise ValueError("No font tables found in the C file")
font_name = table_names[0]
print(f"Font name extracted: {font_name}")
table = load_table(font_name)
identifier = get_value(table, 0)
first_character_lsb = get_value(table, 2)
first_character_msb = get_value(table, 3)
first_character = (first_character_msb << 8) | first_character_lsb
last_character_lsb = get_value(table, 4)
last_character_msb = get_value(table, 5)
last_character = (last_character_msb << 8) | last_character_lsb
max_height = get_value(table, 6)
proportional_width = False if get_value(table, 7) == 1 else True
print("identifier:", identifier)
print("first_character:", first_character)
print("last_character:", last_character)
print("max_height:", max_height)
print("proportional_width:", proportional_width)
number_of_characters = last_character - first_character + 1
with open(args.output_rs, "w", encoding="utf-8") as f:
f.write("/// This file contains the lookup table for the font characters and bitmap data.\n")
f.write("\n")
f.write("use embedded_graphics_profont::font::{Font, GlyphEntry};\n\n")
rust_font_name = font_name.replace("-", "_")
rust_upper = rust_font_name.upper()
f.write("pub const %s: Font = Font {\n" % rust_upper)
f.write(" lookup_table: &%s_LOOKUP_TABLE,\n" % rust_upper)
f.write(" data: &%s_DATA,\n" % rust_upper)
f.write(" ascii_begin: %d,\n" % first_character)
f.write(" ascii_end: %d,\n" % last_character)
f.write(" max_height: %d,\n" % max_height)
f.write(" proportional_width: %s,\n" % str(proportional_width).lower())
f.write("};\n\n")
f.write("pub static %s_LOOKUP_TABLE: [GlyphEntry; %d] = [\n" % (rust_upper, number_of_characters))
for i in range(0, number_of_characters * CHAR_HEADER_LEN, CHAR_HEADER_LEN):
char_code = first_character + i // CHAR_HEADER_LEN
char_width = get_value(table, NUMBER_OF_METADATA_ENTRIES + i)
raw_lsb = get_value(table, NUMBER_OF_METADATA_ENTRIES + i + 1)
raw_msb = get_value(table, NUMBER_OF_METADATA_ENTRIES + i + 2)
char_location = ((raw_msb << 8) | raw_lsb) - NUMBER_OF_METADATA_ENTRIES - number_of_characters * CHAR_HEADER_LEN
if char_code == 32: if i + CHAR_HEADER_LEN < number_of_characters * CHAR_HEADER_LEN:
next_raw_lsb = get_value(table, NUMBER_OF_METADATA_ENTRIES + i + CHAR_HEADER_LEN + 1)
next_raw_msb = get_value(table, NUMBER_OF_METADATA_ENTRIES + i + CHAR_HEADER_LEN + 2)
next_char_location = ((next_raw_msb << 8) | next_raw_lsb) - NUMBER_OF_METADATA_ENTRIES - number_of_characters * CHAR_HEADER_LEN
space_data_bytes = next_char_location - char_location
char_width = (space_data_bytes * 8) // max_height
f.write(" GlyphEntry { width: 0x%02X, offset: 0x%04X }, // Character: %s (ASCII %d)\n" %
(char_width, char_location, chr(char_code), char_code))
f.write("];\n")
f.write("\n")
size_of_character_data = len(table) - NUMBER_OF_METADATA_ENTRIES - number_of_characters * CHAR_HEADER_LEN
f.write("pub static %s_DATA: [u8; %d] = [\n" % (rust_upper, size_of_character_data))
for letter in range(number_of_characters):
char_code = first_character + letter
base = NUMBER_OF_METADATA_ENTRIES + letter * 4
char_width = get_value(table, base)
char_location_lsb = get_value(table, base + 1)
char_location_msb = get_value(table, base + 2)
char_location = (char_location_msb << 8) | char_location_lsb
columns_of_bytes = (char_width + 7) // 8
number_of_bytes = columns_of_bytes * max_height
if letter < number_of_characters - 1:
next_base = NUMBER_OF_METADATA_ENTRIES + (letter + 1) * 4
char_location_next_base_lsb = get_value(table, next_base + 1)
char_location_next_base_msb = get_value(table, next_base + 2)
char_location_next_base = (char_location_next_base_msb << 8) | char_location_next_base_lsb
verif_number_of_bytes = char_location_next_base - char_location
if verif_number_of_bytes != number_of_bytes:
print(
f"Error: Character {char_code} has an unexpected number of bytes. "
f"Expected {number_of_bytes}, but got {verif_number_of_bytes}."
)
else:
print(f"Character {char_code} has a valid number of bytes: {number_of_bytes}.")
else:
print(f"Character {char_code} is the last character, skipping byte count verification.")
first_line = True
for byte_index in range(number_of_bytes):
char_reversed = reverse_bits_u8(get_value(table, char_location + byte_index))
end_of_line = (byte_index + 1) % max_height == 0
is_last = byte_index == number_of_bytes - 1
if first_line:
f.write("\t0x%02X, " % char_reversed)
first_line = False
else:
f.write("0x%02X, " % char_reversed)
if is_last:
f.write(" // Character: %s (ASCII %d)\n" % (chr(char_code), char_code))
first_line = True
elif end_of_line:
f.write("\n")
first_line = True
f.write("];\n")
if __name__ == "__main__":
main()