feat(xml): load fonts and images from XML

This commit is contained in:
Gabor Kiss-Vamosi
2025-03-10 22:51:25 +01:00
parent 2ca425c411
commit 670845ecbc
11 changed files with 164 additions and 19 deletions
@@ -163,6 +163,6 @@ A ``globals.xml`` file of a component library can look like this:
</images>
<fonts>
<tinyttf name="big" src="A:/fonts/arial.ttf" size="28"/>
<tiny_ttf name="big" src="A:/fonts/arial.ttf" size="28"/>
</fonts>
</globals>
@@ -29,7 +29,7 @@ In ``<styles>`` and ``<view>``, fonts can be referenced by their name, e.g.,
<style name="style1" text_font="medium"/>
The tag name determines how the font is loaded. Currently, only ``tinyttf as_file="true"`` is supported.
The tag name determines how the font is loaded. Currently, only ``tiny_ttf as_file="true"`` is supported.
- ``bin``:
@@ -38,7 +38,7 @@ The tag name determines how the font is loaded. Currently, only ``tinyttf as_fil
- If ``as_file="false"`` (default): On export, the font file will be converted to a C array LVGL font
that can be used directly by LVGL.
- ``tinyttf``:
- ``tiny_ttf``:
- If ``as_file="true"``: Can be loaded directly by ``lv_tiny_ttf_create_file()``.
- If ``as_file="false"`` (default): The font file will be converted to a raw C array on export
+1 -1
View File
@@ -15,7 +15,7 @@ void lv_example_xml_2(void)
lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_button.xml");
lv_xml_component_register_from_file("A:lvgl/examples/others/xml/view.xml");
lv_xml_register_font("lv_montserrat_18", &lv_font_montserrat_18);
lv_xml_register_font(NULL, "lv_montserrat_18", &lv_font_montserrat_18);
lv_obj_t * obj = lv_xml_create(lv_screen_active(), "view", NULL);
lv_obj_set_pos(obj, 10, 10);
+5 -5
View File
@@ -195,7 +195,7 @@ const lv_font_t * lv_xml_get_font(lv_xml_component_ctx_t * ctx, const char * nam
}
/*If not found in the component check the global space*/
if(!lv_streq(ctx->name, "globals")) {
if(ctx == NULL || !lv_streq(ctx->name, "globals")) {
ctx = lv_xml_component_get_ctx("globals");
if(ctx) {
LV_LL_READ(&ctx->font_ll, f) {
@@ -233,7 +233,7 @@ lv_subject_t * lv_xml_get_subject(lv_xml_component_ctx_t * ctx, const char * nam
}
/*If not found in the component check the global space*/
if(!lv_streq(ctx->name, "globals")) {
if(ctx == NULL || !lv_streq(ctx->name, "globals")) {
ctx = lv_xml_component_get_ctx("globals");
if(ctx) {
LV_LL_READ(&ctx->subjects_ll, s) {
@@ -277,7 +277,7 @@ const char * lv_xml_get_const(lv_xml_component_ctx_t * ctx, const char * name)
}
/*If not found in the component check the global space*/
if(!lv_streq(ctx->name, "globals")) {
if(ctx == NULL || !lv_streq(ctx->name, "globals")) {
ctx = lv_xml_component_get_ctx("globals");
if(ctx) {
LV_LL_READ(&ctx->const_ll, cnst) {
@@ -325,7 +325,7 @@ const void * lv_xml_get_image(lv_xml_component_ctx_t * ctx, const char * name)
}
/*If not found in the component check the global space*/
if(!lv_streq(ctx->name, "globals")) {
if(ctx == NULL || !lv_streq(ctx->name, "globals")) {
ctx = lv_xml_component_get_ctx("globals");
if(ctx) {
LV_LL_READ(&ctx->image_ll, img) {
@@ -367,7 +367,7 @@ lv_event_cb_t lv_xml_get_event_cb(lv_xml_component_ctx_t * ctx, const char * nam
}
/*If not found in the component check the global space*/
if(!lv_streq(ctx->name, "globals")) {
if(ctx == NULL || !lv_streq(ctx->name, "globals")) {
ctx = lv_xml_component_get_ctx("globals");
if(ctx) {
LV_LL_READ(&ctx->event_ll, e) {
+115 -1
View File
@@ -9,6 +9,7 @@
#include "lv_xml_component.h"
#if LV_USE_XML
#include "../../lvgl.h"
#include "lv_xml_component_private.h"
#include "lv_xml_private.h"
#include "lv_xml_parser.h"
@@ -34,6 +35,8 @@
static void start_metadata_handler(void * user_data, const char * name, const char ** attrs);
static void end_metadata_handler(void * user_data, const char * name);
static void process_const_element(lv_xml_parser_state_t * state, const char ** attrs);
static void process_font_element(lv_xml_parser_state_t * state, const char * type, const char ** attrs);
static void process_image_element(lv_xml_parser_state_t * state, const char * type, const char ** attrs);
static void process_prop_element(lv_xml_parser_state_t * state, const char ** attrs);
static char * extract_view_content(const char * xml_definition);
@@ -242,6 +245,19 @@ lv_result_t lv_xml_component_unregister(const char * name)
lv_ll_clear(&ctx->param_ll);
lv_xml_font_t * font;
LV_LL_READ(&ctx->font_ll, font) {
lv_free((char *)font->name);
}
lv_ll_clear(&ctx->image_ll);
lv_xml_image_t * image;
LV_LL_READ(&ctx->image_ll, image) {
lv_free((char *)image->name);
lv_free((char *)image->src);
}
lv_ll_clear(&ctx->image_ll);
lv_xml_style_t * style;
LV_LL_READ(&ctx->style_ll, style) {
lv_free((char *)style->name);
@@ -294,6 +310,97 @@ static void process_const_element(lv_xml_parser_state_t * state, const char ** a
lv_xml_register_const(&state->ctx, name, value);
}
static void process_font_element(lv_xml_parser_state_t * state, const char * type, const char ** attrs)
{
const char * name = lv_xml_get_value_of(attrs, "name");
if(name == NULL) {
LV_LOG_WARN("'name' is missing from a font");
return;
}
const char * src_path = lv_xml_get_value_of(attrs, "src_path");
if(src_path == NULL) {
LV_LOG_WARN("'src_path' is missing from a `%s` font", name);
return;
}
const char * as_file = lv_xml_get_value_of(attrs, "as_file");
if(as_file == NULL || as_file == false) {
LV_LOG_INFO("Ignore non-file based font `%s`", name);
return;
}
/*E.g. <tiny_ttf name="inter_xl" src_path="fonts/Inter-SemiBold.ttf" size="22"/> */
if(lv_streq(type, "tiny_ttf")) {
const char * size = lv_xml_get_value_of(attrs, "size");
if(size == NULL) {
LV_LOG_WARN("'size' is missing from a `%s` tiny_ttf font", name);
return;
}
#if LV_TINY_TTF_FILE_SUPPORT
lv_font_t * font = lv_tiny_ttf_create_file(src_path, lv_xml_atoi(size));
if(font == NULL) {
LV_LOG_WARN("Couldn't load `%s` tiny_ttf font", name);
return;
}
lv_result_t res = lv_xml_register_font(&state->ctx, name, font);
if(res == LV_RESULT_INVALID) {
LV_LOG_WARN("Failed to register `%s` tiny_ttf font", name);
return;
}
lv_xml_font_t * f = lv_ll_get_head(&state->ctx.font_ll);
f->font_destroy_cb = lv_tiny_ttf_destroy;
#else
LV_LOG_WARN("LV_TINY_TTF_FILE_SUPPORT is not enabled for `%s` font", name);
#endif
}
else if(lv_streq(type, "bin")) {
lv_font_t * font = lv_binfont_create(src_path);
if(font == NULL) {
LV_LOG_WARN("Couldn't load `%s` bin font", name);
return;
}
lv_result_t res = lv_xml_register_font(&state->ctx, name, font);
if(res == LV_RESULT_INVALID) {
LV_LOG_WARN("Failed to register `%s` bin font", name);
return;
}
lv_xml_font_t * f = lv_ll_get_head(&state->ctx.font_ll);
f->font_destroy_cb = lv_binfont_destroy;
}
else {
LV_LOG_WARN("`%s` is a not supported font type", type);
}
}
static void process_image_element(lv_xml_parser_state_t * state, const char * type, const char ** attrs)
{
const char * name = lv_xml_get_value_of(attrs, "name");
if(name == NULL) {
LV_LOG_WARN("'name' is missing from a font");
return;
}
const char * src_path = lv_xml_get_value_of(attrs, "src_path");
if(src_path == NULL) {
LV_LOG_WARN("'src_path' is missing from a `%s` font", name);
return;
}
/* E.g. <file name="avatar" src_path="avatar1.png">*/
if(lv_streq(type, "file")) {
lv_xml_register_image(&state->ctx, name, src_path);
}
else {
LV_LOG_INFO("Ignore non-file image `%s`", name);
}
}
static void process_subject_element(lv_xml_parser_state_t * state, const char * type, const char ** attrs)
{
const char * name = lv_xml_get_value_of(attrs, "name");
@@ -310,7 +417,6 @@ static void process_subject_element(lv_xml_parser_state_t * state, const char *
lv_subject_t * subject = lv_malloc(sizeof(lv_subject_t));
if(lv_streq(type, "int")) lv_subject_init_int(subject, lv_xml_atoi(value));
else if(lv_streq(type, "color")) lv_subject_init_color(subject, lv_xml_to_color(value));
else if(lv_streq(type, "string")) {
@@ -540,6 +646,14 @@ static void start_metadata_handler(void * user_data, const char * name, const ch
if(old_section != state->section) return; /*Ignore the section opening, e.g. <styles>*/
lv_xml_style_register(&state->ctx, attrs);
break;
case LV_XML_PARSER_SECTION_FONTS:
if(old_section != state->section) return; /*Ignore the section opening, e.g. <styles>*/
process_font_element(state, name, attrs);
break;
case LV_XML_PARSER_SECTION_IMAGES:
if(old_section != state->section) return; /*Ignore the section opening, e.g. <styles>*/
process_image_element(state, name, attrs);
break;
case LV_XML_PARSER_SECTION_SUBJECTS:
if(old_section != state->section) return; /*Ignore the section opening, e.g. <subjects>*/
+8
View File
@@ -69,6 +69,14 @@ void lv_xml_parser_start_section(lv_xml_parser_state_t * state, const char * nam
state->section = LV_XML_PARSER_SECTION_STYLES;
return;
}
else if(lv_streq(name, "images")) {
state->section = LV_XML_PARSER_SECTION_IMAGES;
return;
}
else if(lv_streq(name, "fonts")) {
state->section = LV_XML_PARSER_SECTION_FONTS;
return;
}
else if(lv_streq(name, "subjects")) {
state->section = LV_XML_PARSER_SECTION_SUBJECTS;
return;
+2
View File
@@ -35,6 +35,8 @@ typedef enum {
LV_XML_PARSER_SECTION_GRAD,
LV_XML_PARSER_SECTION_GRAD_STOP,
LV_XML_PARSER_SECTION_STYLES,
LV_XML_PARSER_SECTION_FONTS,
LV_XML_PARSER_SECTION_IMAGES,
LV_XML_PARSER_SECTION_SUBJECTS,
LV_XML_PARSER_SECTION_VIEW
} lv_xml_parser_section_t;
+1
View File
@@ -33,6 +33,7 @@ extern "C" {
typedef struct {
const char * name;
const lv_font_t * font;
void (*font_destroy_cb)(lv_font_t *);
} lv_xml_font_t;
typedef struct {
+1
View File
@@ -90,6 +90,7 @@
#define LV_USE_OBSERVER 1
#define LV_USE_FILE_EXPLORER 1
#define LV_USE_TINY_TTF 1
#define LV_TINY_TTF_FILE_SUPPORT 1
#define LV_USE_SYSMON 1
#define LV_USE_MEM_MONITOR 1
#define LV_USE_PERF_MONITOR 1
+23 -9
View File
@@ -1,15 +1,29 @@
<globals>
<config name="mylib" help="This is my great component library"/>
<globals>
<config name="mylib" help="This is my great component library" />
<consts>
<int name="global_int" value="30"/>
</consts>
<consts>
<int name="global_int" value="30" />
</consts>
<styles>
<style name="global_red" bg_color="0xf00" radius="global_small_unit" pad_all="12px"/>
</styles>
<styles>
<style name="global_red" bg_color="0xf00"
radius="global_small_unit" pad_all="12px" />
</styles>
<subjects>
<int name="global_subject" value="22"/>
<int name="global_subject" value="22" />
</subjects>
<images>
<file name="logo"
src_path="A:src/test_assets/test_img_lvgl_logo.png" />
</images>
<fonts>
<bin name="my_bin_font" src_path="A:src/test_assets/test_font_2.fnt" as_file="true"/>
<tiny_ttf name="noto_32"
src_path="A:src/test_files/fonts/noto/NotoSansSC-Regular.ttf"
size="32" as_file="true"/>
</fonts>
</globals>
@@ -22,6 +22,11 @@ void test_xml_view3_scoping(void)
lv_xml_create(lv_screen_active(), "view3", NULL);
TEST_ASSERT_EQUAL_SCREENSHOT("xml/view3.png");
/*Fonts and image defined in globals.xml*/
TEST_ASSERT_NOT_EQUAL(lv_font_get_default(), lv_xml_get_font(NULL, "my_bin_font"));
TEST_ASSERT_NOT_EQUAL(lv_font_get_default(), lv_xml_get_font(NULL, "noto_32"));
TEST_ASSERT_EQUAL_STRING("A:src/test_assets/test_img_lvgl_logo.png", lv_xml_get_image(NULL, "logo"));
}
#endif