#!/usr/bin/env python3 # /*************************************************************************** # * Copyright (C) 2023 by Ryan Turner K0RET * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU General Public License as published by * # * the Free Software Foundation; either version 3 of the License, or * # * (at your option) any later version. * # * * # * This program is distributed in the hope that it will be useful, * # * but WITHOUT ANY WARRANTY; without even the implied warranty of * # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * # * GNU General Public License for more details. * # * * # * You should have received a copy of the GNU General Public License * # * along with this program; if not, see * # ***************************************************************************/ # # Usage: # # 1. Find your source svg; https://pictogrammers.com/library/mdi/ is a good resource # 2. Add your source svg to openrtx/include/fonts/symbols/sources # 3. Execute this script; the corresponding `.h` files in the openrtx/include/fonts/symbols directory will regenerate # 4. Refer to the `symbols/symbols.h` file to find the enum that corresponds with your new symbol # 5. Utilize the `gfx_printSymbol()` method with your enum to access your symbol import cairosvg import io from PIL import Image import numpy import os absolute_path = os.path.dirname(__file__) symbol_source_dir = "../openrtx/include/fonts/symbols/sources/" debug = False sizes = [5, 6, 8] symbols = sorted([f for f in os.listdir(os.path.join(absolute_path, symbol_source_dir)) if "svg" in f]) print(symbols) ################################################################### def svgToBytes(filename, height): # Make memory buffer mem = io.BytesIO() # Convert SVG to PNG in memory cairosvg.svg2png( url=filename, write_to=mem, parent_height=height, parent_width=height ) # Convert PNG to Numpy array img = Image.open(mem) # Split the channels, get just the alpha since that's what we need red, green, blue, alpha = img.split() # Convert the alpha to 1 and 0 (black and white) alpha = alpha.point(lambda x: 0 if x < 128 else 255, "1") if debug: alpha.show() return numpy.array(alpha) first_ascii = 32 # Start at ascii d32 which is " " per https://www.rapidtables.com/code/text/ascii-table.html dont_edit_banner = "// This is a generated file, please do not edit it! Use generate_symbols.py\n" class FontDefinition: scalar = 1.6 # this number is used to roughly convert from pt to px yadvance_scalar = ( 7 / 3 ) # this number is applied to the width to scale up the yadvance; this is naively linear def __init__(self, name, size): # Prepopulate with a space character self.bitmaps = [[0x00]] self.glyphNames = ["space"] self.initial_offset = 1 self.size = size self.name = name self.height = round(size * self.scalar) self.width = round(size * self.scalar) self.yadvance = round(size * self.scalar * self.yadvance_scalar) self.xadvance = round(size * self.scalar * 2 / 3) self.dY = round(-1 * self.height / 1.2) def addGlyph(self, filename, glyphName): res = svgToBytes(filename, self.height) packed = numpy.packbits(res) charBytes = packed.tobytes() self.bitmaps.append(charBytes) self.glyphNames.append(glyphName) def __str__(self): out = dont_edit_banner out += "static const uint8_t {}{}pt7bBitmaps[] PROGMEM = {{\n".format( self.name, self.size ) for i, bitmap in enumerate(self.bitmaps): out += ( (", ".join("0x{:02x}".format(x) for x in bitmap)) + ", //" + self.glyphNames[i] + "\n" ) out += "};\n" out += "\n" out += ( "static const GFXglyph {}{}pt7bGlyphs[] PROGMEM = {{".format( self.name, self.size ) + "\n" ) out += " //Index, W, H,xAdv,dX, dY\n" offset = 0 for glyphNumber, glyphName in enumerate(self.glyphNames): out += '{{ {}, {}, {}, {}, 0, {}}}, // "{}" {}\n'.format( offset, self.width if glyphNumber > 0 else 1, self.height if glyphNumber > 0 else 1, self.xadvance, self.dY, chr(first_ascii + glyphNumber), glyphName, ) offset += len(self.bitmaps[glyphNumber]) out += "};\n" out += ( "static const GFXfont {}{}pt7b PROGMEM = {{".format(self.name, self.size) + "\n" ) out += "(uint8_t *){}{}pt7bBitmaps,".format(self.name, self.size) + "\n" out += "(GFXglyph *){}{}pt7bGlyphs,".format(self.name, self.size) + "\n" out += "//ASCII start, ASCII stop,y Advance \n" out += ( " {}, {}, {} }};".format( first_ascii, first_ascii + len(self.glyphNames) - 1, round(self.yadvance), ) + "\n" ) return out ################################################################### # Generate the font definitions for size in sizes: font = FontDefinition("Symbols", size) for symbol in symbols: font.addGlyph(os.path.join(absolute_path, symbol_source_dir + symbol), symbol) with open(os.path.join(absolute_path, "../openrtx/include/fonts/symbols/Symbols{}pt7b.h".format(size)), "w") as f: f.write(str(font)) # Generate the enum for convenience with open(os.path.join(absolute_path, "../openrtx/include/fonts/symbols/symbols.h".format(size)), "w") as f: out = dont_edit_banner out += "typedef enum {\n" symbols.insert(0, "SPACE") for symbolNumber, symbolName in enumerate(symbols): out += " SYMBOL_{} = {},\n".format(symbolName.split(".")[0].replace("-", "_").upper(), symbolNumber + first_ascii) out += "} symbol_t;\n" f.write(out)