#!/usr/bin/env python ''' Updates icon font and header files for CopyQ repository. First argument is path to upacked Font Awesome archive (https://fontawesome.com/). ''' import json import os import sys from shutil import copyfile from textwrap import dedent from fontTools.merge import Merger from fontTools.ttLib import TTFont FONTS = [ 'fa-solid-900.ttf', 'fa-brands-400.ttf', ] FONT_FILENAME = 'fontawesome.ttf' SOLID_STYLE = 'solid' BRANDS_STYLE = 'brands' def read_icons(icons_json): with open(icons_json, 'r') as icons_file: icons_content = icons_file.read() return json.loads(icons_content) def write_header_file_preamble(header_file): script = os.path.realpath(__file__) script_name = os.path.basename(script) comment = ( f'// This file is generated with "{script_name}"' + ' from FontAwesome\'s metadata.\n\n') header_file.write(comment) def write_icon_list_header_file(header_icon_list, icons): with open(header_icon_list, 'w') as header_file: items = [] for style in [SOLID_STYLE, BRANDS_STYLE]: is_brand = 'true' if style == BRANDS_STYLE else 'false' for name, icon in icons.items(): if style in icon['styles']: code = icon['unicode'] search_terms = [icon['label'].lower()] + (icon['search']['terms'] or []) search_terms_list = '|'.join(search_terms) items.append('{0x%s, %s, "%s"}' % (code, is_brand, search_terms_list)) item_list_content = ',\n'.join(items) content = dedent('''\ struct Icon { unsigned short unicode; bool isBrand; const char *searchTerms; }; constexpr Icon iconList[] = { %s }; ''') % item_list_content write_header_file_preamble(header_file) header_file.write(content) def write_icons_header_file(header_icons, icons): with open(header_icons, 'w') as header_file: write_header_file_preamble(header_file) header_file.write('#ifndef ICONS_H\n') header_file.write('#define ICONS_H\n') header_file.write('\n') header_file.write('enum IconId {\n') for name, icon in icons.items(): label = name.title().replace('-', '') code = icon['unicode'] header_file.write(f' Icon{label} = 0x{code},' + '\n') header_file.write('};\n') header_file.write('\n') header_file.write('#endif // ICONS_H\n') def rename_font_family(path): """ Adds suffix to font family it doesn't conflict with font installed on system, which could be in incorrect version. See: https://github.com/fonttools/fonttools/blob/master/Snippets/rename-fonts.py """ font = TTFont(path) name_table = font['name'] FAMILY_RELATED_IDS = dict( LEGACY_FAMILY=1, TRUETYPE_UNIQUE_ID=3, FULL_NAME=4, POSTSCRIPT_NAME=6, PREFERRED_FAMILY=16, WWS_FAMILY=21, ) for rec in name_table.names: if rec.nameID not in FAMILY_RELATED_IDS.values(): continue name = rec.toUnicode() if name.startswith('Font Awesome'): rec.string = name + ' (CopyQ)' elif name.startswith('FontAwesome'): rec.string = name + '(CopyQ)' assert rec.toUnicode().endswith('(CopyQ)') font.save(path) def copy_fonts(font_awesome_src, target_font_dir): font_dir = os.path.join(font_awesome_src, 'webfonts') fonts = [ os.path.join(font_dir, src_name) for src_name in FONTS ] dest_path = os.path.join(target_font_dir, FONT_FILENAME) print(f'Merging fonts: {fonts} -> {dest_path}') merger = Merger() with merger.merge(fonts) as font: font.save(dest_path) rename_font_family(dest_path) def main(): font_awesome_src = sys.argv[1] script = os.path.realpath(__file__) utils_dir = os.path.dirname(script) project_dir = os.path.dirname(utils_dir) src_dir = os.path.join(project_dir, 'src') header_icon_list = os.path.join(src_dir, 'gui', 'icon_list.h') header_icons = os.path.join(src_dir, 'gui', 'icons.h') target_font_dir = os.path.join(src_dir, 'images') copy_fonts(font_awesome_src, target_font_dir) icons_json = os.path.join( font_awesome_src, 'metadata', 'icons.json') icons = read_icons(icons_json) write_icon_list_header_file(header_icon_list, icons) print(f'Header file "{header_icon_list}" updated.') write_icons_header_file(header_icons, icons) print(f'Header file "{header_icons}" updated.') if __name__ == '__main__': main()