# coding=utf-8 from esphome import core import esphome.config_validation as cv import esphome.codegen as cg from esphome.const import CONF_ID, CONF_GLYPHS, CONF_DATA from esphome.core import CORE, HexInt import logging #from esphome.py_compat import sort_by_cmp _LOGGER = logging.getLogger(__name__) DOMAIN = "font" DEPENDENCIES = ['display'] MULTI_CONF = True font_ns = cg.esphome_ns.namespace("font") Font = font_ns.class_("Font") Glyph = font_ns.class_("Glyph") GlyphData = font_ns.struct("GlyphData") def validate_data(value): r = type(value)() for k, v in value.items(): if isinstance(k, str): try: k = int(k) except ValueError: if len(k.encode()) > 1: _LOGGER.warning(f"multi byte character: '{k}'") continue k = ord(k.encode()) if k>=0 and k<=9: k += ord('0') r[k] = v return type(value)(sorted(r.items())) CONF_RAW_DATA_ID = 'raw_data_id' CONF_RAW_GLYPH_ID = "raw_glyph_id" BIT0_DEFAULT = '0 ' RASTERFONT_SCHEMA = cv.Schema({ cv.Required(CONF_ID): cv.declare_id(Font), cv.Required(CONF_DATA): validate_data, cv.Optional(CONF_GLYPHS): cv.string_strict, cv.Optional('bit0', default=BIT0_DEFAULT): cv.string_strict, cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), cv.GenerateID(CONF_RAW_GLYPH_ID): cv.declare_id(GlyphData), }) CONFIG_SCHEMA = cv.All(RASTERFONT_SCHEMA) def string_to_int(s, bit0): b = ''.join(map(lambda x: '0' if x in bit0 else '1', s+'0'*((8-len(s) % 8) % 8))) r = [int(b[i:i+8], 2) for i in range(0, len(b), 8)] return r def int_to_cchar(i): if i >= 32 and i <= 126: return '"%c"' % i else: return '"\\x%02x"' % i def to_code(config): glyph_args = {} data = [] include_glyphs = config.get(CONF_GLYPHS, None) bit0 = config.get('bit0') for glyph, gconfig in config[CONF_DATA].items(): if include_glyphs and glyph not in include_glyphs: continue height = 0 maxwidth = 0 offset_x = gconfig.get('offset_x', 0) offset_y = gconfig.get('offset_y', 0) rows = gconfig.get('bitmap', []) if isinstance(rows, str): rows = rows.split('\n') while rows[-1] == '': rows.pop() for row in rows: maxwidth = max(maxwidth, len(row)) width = gconfig.get('width', maxwidth) glyph_data = [] for row in rows: height += 1 glyph_data += string_to_int(row + '0'*(maxwidth-len(row)), bit0) width8 = ((width + 7) // 8) * 8 glyph_args[glyph] = (len(data), offset_x, offset_y, width, height) data += glyph_data rhs = [HexInt(x) for x in data] prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) glyph_initializer = [] for glyph in config[CONF_DATA].keys(): glyph_initializer.append( cg.StructInitializer( GlyphData, ("a_char", cg.RawExpression(int_to_cchar(glyph))), ( "data", cg.RawExpression(str(prog_arr) + " + " + str(glyph_args[glyph][0])), ), ("offset_x", glyph_args[glyph][1]), ("offset_y", glyph_args[glyph][2]), ("width", glyph_args[glyph][3]), ("height", glyph_args[glyph][4]), ) ) glyphs = cg.static_const_array(config[CONF_RAW_GLYPH_ID], glyph_initializer) cg.new_Pvariable( config[CONF_ID], glyphs, len(glyph_initializer), 0, 0 )