diff --git a/utilities/font/analyze_and_add_spanish_chars.py b/utilities/font/analyze_and_add_spanish_chars.py deleted file mode 100755 index bd46114a..00000000 --- a/utilities/font/analyze_and_add_spanish_chars.py +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import re -import subprocess -import os -import glob - -# --- Configuración --- -target_font_file = 'Montserrat-Medium.ttf' # Fuente TTF -output_dir = 'out' # Directorio de salida - -# Caracteres españoles a agregar (puntos Unicode) -spanish_unicode_points = [161, 191, 193, 201, 205, 209, 211, 218, 220, 225, 233, 237, 241, 243, 250, 252] - -def extract_simplified_chinese_unicode(): - """Extrae caracteres chinos del archivo zh_hans.ini""" - input_file_path = "../../mkapp/app/language/zh_hans.ini" - range_str = "" - char_pattern = re.compile(r'[\u4e00-\u9fff]') - unique_chars = set() - - try: - with open(input_file_path, "r", encoding="utf-8") as file: - for line in file: - for char in line: - if char_pattern.match(char): - unique_chars.add(char) - - for char in sorted(unique_chars): - range_str += f"{ord(char)}," - - return range_str[:-1] - except FileNotFoundError: - print(f"Warning: {input_file_path} not found. Using empty Chinese range.") - return "" - -def update_range_with_spanish(original_range): - """Actualiza un rango agregando los caracteres españoles""" - if not original_range: - return original_range - - # Convertir el rango original a lista de números - existing_points = set() - for part in original_range.split(','): - if '-' in part: - start, end = map(int, part.split('-')) - existing_points.update(range(start, end + 1)) - else: - existing_points.add(int(part)) - - # Agregar los caracteres españoles - new_points = existing_points.union(set(spanish_unicode_points)) - - # Convertir de vuelta a formato de rango - sorted_points = sorted(new_points) - ranges = [] - i = 0 - while i < len(sorted_points): - start = sorted_points[i] - while i + 1 < len(sorted_points) and sorted_points[i + 1] == sorted_points[i] + 1: - i += 1 - end = sorted_points[i] - - if start == end: - ranges.append(str(start)) - else: - ranges.append(f"{start}-{end}") - i += 1 - - return ','.join(ranges) - -def process_font_file(input_file): - """Procesa un archivo de fuente individual""" - output_file = os.path.join(output_dir, os.path.basename(input_file)) - - print(f"\nProcesando: {input_file}") - - try: - # Extraer la línea de comando original del archivo C - with open(input_file, 'r', encoding='utf-8') as f: - content = f.read() - - # Buscar la línea Opts que contiene el comando original - opts_match = re.search(r'\*\s*Opts:\s*(.*)', content) - if not opts_match: - print(f" Warning: No se encontró la línea 'Opts:' en {input_file}") - return False - - original_command = opts_match.group(1).strip() - - # Extraer el rango actual de Montserrat - montserrat_range_match = re.search(r'--font\s+Montserrat-Medium\.ttf\s+--range\s+([\d,-]+)', original_command) - if not montserrat_range_match: - print(f" Warning: No se encontró el rango de Montserrat en {input_file}") - return False - - original_range = montserrat_range_match.group(1) - print(f" Rango original: {original_range}") - - # Actualizar el rango con caracteres españoles - updated_range = update_range_with_spanish(original_range) - print(f" Rango actualizado: {updated_range}") - - # Actualizar el comando original con cuidado - new_command = original_command.replace( - f"--font Montserrat-Medium.ttf --range {original_range}", - f"--font Montserrat-Medium.ttf --range {updated_range}" - ) - - # Actualizar la ruta de salida - basename = os.path.basename(input_file) - new_command = new_command.replace( - f"-o out/{basename}", - f"-o {output_file}" - ) - - print(f" Generando nueva fuente...") - # Usar subprocess.run sin shell=True para evitar problemas de parsing - cmd_parts = ['lv_font_conv'] + new_command.split() - result = subprocess.run(cmd_parts, capture_output=True, text=True) - - if result.returncode == 0: - print(f" ✓ Fuente generada exitosamente: {output_file}") - return True - else: - print(f" ✗ Error al generar la fuente: {result.stderr}") - if result.stdout: - print(f" Output: {result.stdout}") - return False - - except Exception as e: - print(f" ✗ Error: {e}") - return False - -def main(): - # Crear directorio de salida si no existe - if not os.path.exists(output_dir): - os.makedirs(output_dir) - print(f"Creado directorio de salida: {output_dir}") - - # Encontrar todos los archivos lv_font_montserrat_*.c - font_files = glob.glob("out/lv_font_montserrat_*.c") - - if not font_files: - print("No se encontraron archivos lv_font_montserrat_*.c en el directorio actual") - return - - print(f"Encontrados {len(font_files)} archivos de fuente:") - for f in font_files: - print(f" - {f}") - - # Procesar cada archivo - success_count = 0 - failed_count = 0 - - for font_file in font_files: - if process_font_file(font_file): - success_count += 1 - else: - failed_count += 1 - - # Resumen - print("\n" + "="*50) - print(f"Procesamiento completado:") - print(f" ✓ Exitosos: {success_count}") - print(f" ✗ Fallidos: {failed_count}") - print(f" Total: {len(font_files)}") - print("="*50) - - if success_count > 0: - print(f"\nLas fuentes actualizadas están en: {output_dir}/") - print("Recuerda copiar los archivos al directorio de fuentes del proyecto:") - print(f" cp {output_dir}/* ../../lib/lvgl/lvgl/src/font/") - -if __name__ == "__main__": - main() diff --git a/utilities/font/copy-fonts.bash b/utilities/font/copy-fonts.bash deleted file mode 100755 index fca06b20..00000000 --- a/utilities/font/copy-fonts.bash +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -SOURCE_DIR="./out" -TARGET_DIR="../../lib/lvgl/lvgl/src/font" - -echo "Copying fonts..." -cp "$SOURCE_DIR"/*.c "$TARGET_DIR" -if [ $? -eq 0 ]; then - echo "Font files copied successfully." -else - echo "Error while copying files." -fi diff --git a/utilities/font/generate_font_lib.py b/utilities/font/generate_font_lib.py index 45f751dc..91df736a 100755 --- a/utilities/font/generate_font_lib.py +++ b/utilities/font/generate_font_lib.py @@ -1,25 +1,35 @@ +import argparse +import json import re +import shutil import subprocess import os +from pathlib import Path -def extract_simplified_chinese_unicode(): - input_file_path = "../../mkapp/app/language/zh_hans.ini" - range_str = "" - char_pattern = re.compile(r'[\u4e00-\u9fff]') +def extract_unicode_points(input_file: Path, char_pattern: re.Pattern = re.compile('.')) -> list[int]: + codes: list[int] = [] - unique_chars = set() - - with open(input_file_path, "r", encoding="utf-8") as file: + with open(input_file, 'r', encoding='utf-8') as file: for line in file: - for char in line: + if line.count('"') == 0: + continue + text = line[line.find('"') + 1:line.rfind('"')] + for char in text: if char_pattern.match(char): - unique_chars.add(char) + code_point = ord(char) + if not code_point in codes: + codes.append(code_point) + + return sorted(codes) + + +def list_to_plain_string(list: list[int]) -> str: + return ",".join(map(str, list)) - for char in sorted(unique_chars): - range_str += f"{ord(char)}," - return range_str[:-1] +def unicode_points_range(input_file: Path, char_pattern: re.Pattern = re.compile('.')) -> str: + return list_to_plain_string(extract_unicode_points(input_file, char_pattern)) def patch(): @@ -39,51 +49,86 @@ def patch(): file.writelines(lines_to_keep) -cmd_app = "lv_font_conv" -cmd_bpp = " --bpp 4" -cmd_size = " --size " -cmd_compress = " --no-compress" -cmd_format = " --format lvgl" -cmd_output = " -o out/lv_font_montserrat_" - -cmd_font_default = " --font Montserrat-Medium.ttf" -cmd_range_default = " --range 32-127,176,8226" - -cmd_font_lvgl_privite = " --font FontAwesome5-Solid+Brands+Regular.woff" -cmd_range_lvgl_privite = " --range 61441,61448,61451,61452,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650" - -cmd_font_simplified_chinese = " --font simhei.ttf" -cmd_range_simplified_chinese = " --range " + \ - extract_simplified_chinese_unicode() - -cmd_font_cyrillic = " --font Montserrat-Medium.ttf" -cmd_range_cyrillic = " --range 1024-1279" - -font_size = [8, 10, 12, 14, 16, 18, 20, 22, 24, - 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48] - -for s in font_size: - command = "" - command += cmd_app - command += cmd_bpp - command += cmd_size + str(s) - command += cmd_compress - - command += cmd_font_default - command += cmd_range_default - command += cmd_font_lvgl_privite - command += cmd_range_lvgl_privite - command += cmd_font_simplified_chinese - command += cmd_range_simplified_chinese - command += cmd_font_cyrillic - command += cmd_range_cyrillic - - - command += cmd_format - command += cmd_output + str(s) + ".c" - print(command) - subprocess.run(command, text=True, shell=True, capture_output=True) - -print("Patch") -patch() -print("Done") +def generate_fonts(verbose: bool=False): + print("Generating fonts") + + cmd_app = "lv_font_conv" + cmd_bpp = " --bpp 4" + cmd_size = " --size " + cmd_compress = " --no-compress" + cmd_format = " --format lvgl" + cmd_output = " -o out/lv_font_montserrat_" + + cmd_font_default = " --font Montserrat-Medium.ttf" + cmd_range_default = " --range 32-127,176,8226" + + cmd_font_lvgl_privite = " --font FontAwesome5-Solid+Brands+Regular.woff" + cmd_range_lvgl_privite = " --range 61441,61448,61451,61452,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650" + + cmd_languages: str = "" + + with open(Path(__file__).parent / "language_info.json", 'r') as f: + language_info = json.load(f) + + BASE_LANGUAGE_PATH: Path = Path(__file__).parent / "../../mkapp/app/language" + for language in language_info: + cmd_languages += " --font " + language["font"] + " --range " + unicode_points_range(BASE_LANGUAGE_PATH / language["ini"], re.compile(language["range"])) + + font_size = [8, 10, 12, 14, 16, 18, 20, 22, 24, + 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48] + + for s in font_size: + print(f'Generating font size {s}...', end="") + command = "" + command += cmd_app + command += cmd_bpp + command += cmd_size + str(s) + command += cmd_compress + + command += cmd_font_default + command += cmd_range_default + command += cmd_font_lvgl_privite + command += cmd_range_lvgl_privite + command += cmd_languages + + command += cmd_format + command += cmd_output + str(s) + ".c" + if verbose: + print(f'\n{command}') + result = subprocess.run(command, text=True, shell=True, capture_output=True) + if result.returncode != 0: + print(f'{result.stderr}') + raise Exception("Failed to generate font") + else: + print('✓') + + print("Patch...", end="") + patch() + print("Done") + print('=' * 50) + + +def copy_to_target_folder(src: Path, dst: Path): + if not dst.exists(): + dst.mkdir(parents=True) + + try: + for file in src.glob('*.c'): + print(f'Copying {file.name} to {dst.resolve()}') + shutil.copy(file, dst) + + print('Done') + except PermissionError: + print('PermissionError: Need higher privileges (e.g. sudo) to copy files') + print('=' * 50) + + +if __name__ == '__main__': + argument_parser = argparse.ArgumentParser() + argument_parser.add_argument('-c', '--copy', action='store_true', help='Copy generated fonts to target folder') + argument_parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose output') + args = argument_parser.parse_args() + + generate_fonts(args.verbose) + if args.copy: + copy_to_target_folder(Path(__file__).parent / 'out', Path(__file__).parent / '../../lib/lvgl/lvgl/src/font') diff --git a/utilities/font/language_info.json b/utilities/font/language_info.json new file mode 100644 index 00000000..ce072f8a --- /dev/null +++ b/utilities/font/language_info.json @@ -0,0 +1,20 @@ +[ + { + "name": "Simplified Chinese", + "ini": "zh_hans.ini", + "font": "simhei.ttf", + "range": "[\u4E00-\u9FFF]" + }, + { + "name": "Russian", + "ini": "ru_ru.ini", + "font": "Montserrat-Medium.ttf", + "range": "[\u0400-\u04FF]" + }, + { + "name": "Spanish", + "ini": "es_es.ini", + "font": "Montserrat-Medium.ttf", + "range": "[\u00C0-\u00FF]" + } +] \ No newline at end of file diff --git a/utilities/font/readme.md b/utilities/font/readme.md index 07791ae2..355f5994 100644 --- a/utilities/font/readme.md +++ b/utilities/font/readme.md @@ -1,17 +1,54 @@ -## Install npm +# Font generation for non-ascii characters + +To display non-ascii characters in the goggles, you have to generate the font library for lvgl. This is done by doing +three things: + +1. Describe your language in the `language_info.json` file. +1. Run the `generate_font_lib.py` script. +1. Copy the generated font library to the `lib/lvgl/lvgl/src/font` directory. + +## Describing the font + +The `language_info.json` file is a json array of objects, where each object has the following properties: + +- `name`: The name of the language. +- `ini`: The name of the ini file that contains the font. +- `font`: The name of the font file. +- `range`: The range of characters that are supported by the font. You can look those up on the [unicode website](https://https://home.unicode.org/). + +## Running the script + +You can run the script on either a Linux machine or inside the WSL by following these steps. The only prerequesite is +that you have a fairly recent version of Python installed. + +### Install npm ``` sudo apt-get install npm ``` -## Install lv_font_conv +### Install lv_font_conv ``` sudo npm install -g lv_font_conv ``` -## Make font +### Make font ``` python3 generate_font_lib.py ``` + +## Copying the font library + +Copy the generated font library (i.e. all lv_font_*.c files) from the `out` directory to the `lib/lvgl/lvgl/src/font` +directory. Don't forget to commit them to the repository or others won't be able to see all characters necessary by the +localized strings. + +This can also be achieved by calling the script with the `--copy` flag: + +``` +python3 generate_font_lib.py --copy +``` + +Note that this operation might need privileges to copy the files to the target directory.