mirror of
https://github.com/esphome/esphome.git
synced 2025-03-23 11:08:16 +00:00
commit
5c6368b6b8
@ -146,6 +146,13 @@ def check_missing_glyphs(file, codepoints, warning: bool = False):
|
|||||||
raise cv.Invalid(message)
|
raise cv.Invalid(message)
|
||||||
|
|
||||||
|
|
||||||
|
def pt_to_px(pt):
|
||||||
|
"""
|
||||||
|
Convert a point size to pixels, rounding up to the nearest pixel
|
||||||
|
"""
|
||||||
|
return (pt + 63) // 64
|
||||||
|
|
||||||
|
|
||||||
def validate_font_config(config):
|
def validate_font_config(config):
|
||||||
"""
|
"""
|
||||||
Check for duplicate codepoints, then check that all requested codepoints actually
|
Check for duplicate codepoints, then check that all requested codepoints actually
|
||||||
@ -172,42 +179,43 @@ def validate_font_config(config):
|
|||||||
)
|
)
|
||||||
# Make setpoints and glyphspoints disjoint
|
# Make setpoints and glyphspoints disjoint
|
||||||
setpoints.difference_update(glyphspoints)
|
setpoints.difference_update(glyphspoints)
|
||||||
if fileconf[CONF_TYPE] == TYPE_LOCAL_BITMAP:
|
# check that glyphs are actually present
|
||||||
if any(x >= 256 for x in setpoints.copy().union(glyphspoints)):
|
# Check extras against their own font, exclude from parent font codepoints
|
||||||
raise cv.Invalid("Codepoints in bitmap fonts must be in the range 0-255")
|
for extra in config[CONF_EXTRAS]:
|
||||||
else:
|
points = {ord(x) for x in flatten(extra[CONF_GLYPHS])}
|
||||||
# for TT fonts, check that glyphs are actually present
|
glyphspoints.difference_update(points)
|
||||||
# Check extras against their own font, exclude from parent font codepoints
|
setpoints.difference_update(points)
|
||||||
for extra in config[CONF_EXTRAS]:
|
check_missing_glyphs(extra[CONF_FILE], points)
|
||||||
points = {ord(x) for x in flatten(extra[CONF_GLYPHS])}
|
|
||||||
glyphspoints.difference_update(points)
|
|
||||||
setpoints.difference_update(points)
|
|
||||||
check_missing_glyphs(extra[CONF_FILE], points)
|
|
||||||
|
|
||||||
# A named glyph that can't be provided is an error
|
# A named glyph that can't be provided is an error
|
||||||
|
|
||||||
check_missing_glyphs(fileconf, glyphspoints)
|
check_missing_glyphs(fileconf, glyphspoints)
|
||||||
# A missing glyph from a set is a warning.
|
# A missing glyph from a set is a warning.
|
||||||
if not config[CONF_IGNORE_MISSING_GLYPHS]:
|
if not config[CONF_IGNORE_MISSING_GLYPHS]:
|
||||||
check_missing_glyphs(fileconf, setpoints, warning=True)
|
check_missing_glyphs(fileconf, setpoints, warning=True)
|
||||||
|
|
||||||
# Populate the default after the above checks so that use of the default doesn't trigger errors
|
# Populate the default after the above checks so that use of the default doesn't trigger errors
|
||||||
|
font = FONT_CACHE[fileconf]
|
||||||
if not config[CONF_GLYPHS] and not config[CONF_GLYPHSETS]:
|
if not config[CONF_GLYPHS] and not config[CONF_GLYPHSETS]:
|
||||||
if fileconf[CONF_TYPE] == TYPE_LOCAL_BITMAP:
|
# set a default glyphset, intersected with what the font actually offers
|
||||||
config[CONF_GLYPHS] = [DEFAULT_GLYPHS]
|
config[CONF_GLYPHS] = [
|
||||||
else:
|
chr(x)
|
||||||
# set a default glyphset, intersected with what the font actually offers
|
for x in glyphsets.unicodes_per_glyphset(DEFAULT_GLYPHSET)
|
||||||
font = FONT_CACHE[fileconf]
|
if font.get_char_index(x) != 0
|
||||||
config[CONF_GLYPHS] = [
|
]
|
||||||
chr(x)
|
|
||||||
for x in glyphsets.unicodes_per_glyphset(DEFAULT_GLYPHSET)
|
|
||||||
if font.get_char_index(x) != 0
|
|
||||||
]
|
|
||||||
|
|
||||||
if config[CONF_FILE][CONF_TYPE] == TYPE_LOCAL_BITMAP:
|
if font.has_fixed_sizes:
|
||||||
if CONF_SIZE in config:
|
sizes = [pt_to_px(x.size) for x in font.available_sizes]
|
||||||
|
if not sizes:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
"Size is not a valid option for bitmap fonts, which are inherently fixed size"
|
f"Font {FontCache.get_name(fileconf)} has no available sizes"
|
||||||
|
)
|
||||||
|
if CONF_SIZE not in config:
|
||||||
|
config[CONF_SIZE] = sizes[0]
|
||||||
|
elif config[CONF_SIZE] not in sizes:
|
||||||
|
sizes = ", ".join(str(x) for x in sizes)
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"Font {FontCache.get_name(fileconf)} only has size{'s' if len(sizes) != 1 else ''} {sizes} available"
|
||||||
)
|
)
|
||||||
elif CONF_SIZE not in config:
|
elif CONF_SIZE not in config:
|
||||||
config[CONF_SIZE] = 20
|
config[CONF_SIZE] = 20
|
||||||
@ -215,14 +223,7 @@ def validate_font_config(config):
|
|||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
FONT_EXTENSIONS = (".ttf", ".woff", ".otf")
|
FONT_EXTENSIONS = (".ttf", ".woff", ".otf", "bdf", ".pcf")
|
||||||
BITMAP_EXTENSIONS = (".bdf", ".pcf")
|
|
||||||
|
|
||||||
|
|
||||||
def validate_bitmap_file(value):
|
|
||||||
if not any(map(value.lower().endswith, BITMAP_EXTENSIONS)):
|
|
||||||
raise cv.Invalid(f"Only {', '.join(BITMAP_EXTENSIONS)} files are supported.")
|
|
||||||
return CORE.relative_config_path(cv.file_(value))
|
|
||||||
|
|
||||||
|
|
||||||
def validate_truetype_file(value):
|
def validate_truetype_file(value):
|
||||||
@ -246,7 +247,6 @@ def add_local_file(value):
|
|||||||
|
|
||||||
|
|
||||||
TYPE_LOCAL = "local"
|
TYPE_LOCAL = "local"
|
||||||
TYPE_LOCAL_BITMAP = "local_bitmap"
|
|
||||||
TYPE_GFONTS = "gfonts"
|
TYPE_GFONTS = "gfonts"
|
||||||
TYPE_WEB = "web"
|
TYPE_WEB = "web"
|
||||||
LOCAL_SCHEMA = cv.All(
|
LOCAL_SCHEMA = cv.All(
|
||||||
@ -258,15 +258,6 @@ LOCAL_SCHEMA = cv.All(
|
|||||||
add_local_file,
|
add_local_file,
|
||||||
)
|
)
|
||||||
|
|
||||||
LOCAL_BITMAP_SCHEMA = cv.All(
|
|
||||||
cv.Schema(
|
|
||||||
{
|
|
||||||
cv.Required(CONF_PATH): validate_bitmap_file,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
add_local_file,
|
|
||||||
)
|
|
||||||
|
|
||||||
FULLPATH_SCHEMA = cv.maybe_simple_value(
|
FULLPATH_SCHEMA = cv.maybe_simple_value(
|
||||||
{cv.Required(CONF_PATH): cv.string}, key=CONF_PATH
|
{cv.Required(CONF_PATH): cv.string}, key=CONF_PATH
|
||||||
)
|
)
|
||||||
@ -403,15 +394,6 @@ def validate_file_shorthand(value):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
extension = Path(value).suffix
|
|
||||||
if extension in BITMAP_EXTENSIONS:
|
|
||||||
return font_file_schema(
|
|
||||||
{
|
|
||||||
CONF_TYPE: TYPE_LOCAL_BITMAP,
|
|
||||||
CONF_PATH: value,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return font_file_schema(
|
return font_file_schema(
|
||||||
{
|
{
|
||||||
CONF_TYPE: TYPE_LOCAL,
|
CONF_TYPE: TYPE_LOCAL,
|
||||||
@ -424,7 +406,6 @@ TYPED_FILE_SCHEMA = cv.typed_schema(
|
|||||||
{
|
{
|
||||||
TYPE_LOCAL: LOCAL_SCHEMA,
|
TYPE_LOCAL: LOCAL_SCHEMA,
|
||||||
TYPE_GFONTS: GFONTS_SCHEMA,
|
TYPE_GFONTS: GFONTS_SCHEMA,
|
||||||
TYPE_LOCAL_BITMAP: LOCAL_BITMAP_SCHEMA,
|
|
||||||
TYPE_WEB: WEB_FONT_SCHEMA,
|
TYPE_WEB: WEB_FONT_SCHEMA,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -522,11 +503,13 @@ async def to_code(config):
|
|||||||
bpp = config[CONF_BPP]
|
bpp = config[CONF_BPP]
|
||||||
mode = ft_pixel_mode_grays
|
mode = ft_pixel_mode_grays
|
||||||
scale = 256 // (1 << bpp)
|
scale = 256 // (1 << bpp)
|
||||||
|
size = config[CONF_SIZE]
|
||||||
# create the data array for all glyphs
|
# create the data array for all glyphs
|
||||||
for codepoint in codepoints:
|
for codepoint in codepoints:
|
||||||
font = point_font_map[codepoint]
|
font = point_font_map[codepoint]
|
||||||
if not font.has_fixed_sizes:
|
format = font.get_format().decode("utf-8")
|
||||||
font.set_pixel_sizes(config[CONF_SIZE], 0)
|
if format != "PCF":
|
||||||
|
font.set_pixel_sizes(size, 0)
|
||||||
font.load_char(codepoint)
|
font.load_char(codepoint)
|
||||||
font.glyph.render(mode)
|
font.glyph.render(mode)
|
||||||
width = font.glyph.bitmap.width
|
width = font.glyph.bitmap.width
|
||||||
@ -550,17 +533,17 @@ async def to_code(config):
|
|||||||
if pixel & (1 << (bpp - bit_num - 1)):
|
if pixel & (1 << (bpp - bit_num - 1)):
|
||||||
glyph_data[pos // 8] |= 0x80 >> (pos % 8)
|
glyph_data[pos // 8] |= 0x80 >> (pos % 8)
|
||||||
pos += 1
|
pos += 1
|
||||||
ascender = font.size.ascender // 64
|
ascender = pt_to_px(font.size.ascender)
|
||||||
if ascender == 0:
|
if ascender == 0:
|
||||||
if font.has_fixed_sizes:
|
if font.has_fixed_sizes:
|
||||||
ascender = font.available_sizes[0].height
|
ascender = size
|
||||||
else:
|
else:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Unable to determine ascender of font %s", config[CONF_FILE]
|
"Unable to determine ascender of font %s", config[CONF_FILE]
|
||||||
)
|
)
|
||||||
glyph_args[codepoint] = GlyphInfo(
|
glyph_args[codepoint] = GlyphInfo(
|
||||||
len(data),
|
len(data),
|
||||||
font.glyph.metrics.horiAdvance // 64,
|
pt_to_px(font.glyph.metrics.horiAdvance),
|
||||||
font.glyph.bitmap_left,
|
font.glyph.bitmap_left,
|
||||||
ascender - font.glyph.bitmap_top,
|
ascender - font.glyph.bitmap_top,
|
||||||
width,
|
width,
|
||||||
@ -599,11 +582,11 @@ async def to_code(config):
|
|||||||
|
|
||||||
glyphs = cg.static_const_array(config[CONF_RAW_GLYPH_ID], glyph_initializer)
|
glyphs = cg.static_const_array(config[CONF_RAW_GLYPH_ID], glyph_initializer)
|
||||||
|
|
||||||
font_height = base_font.size.height // 64
|
font_height = pt_to_px(base_font.size.height)
|
||||||
ascender = base_font.size.ascender // 64
|
ascender = pt_to_px(base_font.size.ascender)
|
||||||
if font_height == 0:
|
if font_height == 0:
|
||||||
if base_font.has_fixed_sizes:
|
if base_font.has_fixed_sizes:
|
||||||
font_height = base_font.available_sizes[0].height
|
font_height = size
|
||||||
ascender = font_height
|
ascender = font_height
|
||||||
else:
|
else:
|
||||||
_LOGGER.error("Unable to determine height of font %s", config[CONF_FILE])
|
_LOGGER.error("Unable to determine height of font %s", config[CONF_FILE])
|
||||||
|
@ -132,6 +132,10 @@ void Graph::draw(Display *buff, uint16_t x_offset, uint16_t y_offset, Color colo
|
|||||||
yrange = ymax - ymin;
|
yrange = ymax - ymin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store graph limts
|
||||||
|
this->graph_limit_max_ = ymax;
|
||||||
|
this->graph_limit_min_ = ymin;
|
||||||
|
|
||||||
/// Draw grid
|
/// Draw grid
|
||||||
if (!std::isnan(this->gridspacing_y_)) {
|
if (!std::isnan(this->gridspacing_y_)) {
|
||||||
for (int y = yn; y <= ym; y++) {
|
for (int y = yn; y <= ym; y++) {
|
||||||
|
@ -161,11 +161,15 @@ class Graph : public Component {
|
|||||||
uint32_t get_duration() { return duration_; }
|
uint32_t get_duration() { return duration_; }
|
||||||
uint32_t get_width() { return width_; }
|
uint32_t get_width() { return width_; }
|
||||||
uint32_t get_height() { return height_; }
|
uint32_t get_height() { return height_; }
|
||||||
|
float get_graph_limit_min() { return graph_limit_min_; }
|
||||||
|
float get_graph_limit_max() { return graph_limit_max_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint32_t duration_; /// in seconds
|
uint32_t duration_; /// in seconds
|
||||||
uint32_t width_; /// in pixels
|
uint32_t width_; /// in pixels
|
||||||
uint32_t height_; /// in pixels
|
uint32_t height_; /// in pixels
|
||||||
|
float graph_limit_min_{NAN};
|
||||||
|
float graph_limit_max_{NAN};
|
||||||
float min_value_{NAN};
|
float min_value_{NAN};
|
||||||
float max_value_{NAN};
|
float max_value_{NAN};
|
||||||
float min_range_{1.0};
|
float min_range_{1.0};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""Constants used by esphome."""
|
"""Constants used by esphome."""
|
||||||
|
|
||||||
__version__ = "2025.3.0b2"
|
__version__ = "2025.3.0b3"
|
||||||
|
|
||||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||||
VALID_SUBSTITUTIONS_CHARACTERS = (
|
VALID_SUBSTITUTIONS_CHARACTERS = (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user