diff options
| author | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2026-10-13 19:40:44 +0200 |
|---|---|---|
| committer | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2026-10-13 19:40:44 +0200 |
| commit | 35e098cb8764eb5dc9f61a3abac67f971a259a9e (patch) | |
| tree | cc8153411079a59f8bcb601d15d10ce4ac227ba6 | |
| parent | aa9296a0b81c1cc72d42d5367004d7144766f924 (diff) | |
<text> support wip
| -rw-r--r-- | src/svg/svg-parse.c | 54 | ||||
| -rw-r--r-- | src/svg/vkvg_svg_internal.h | 10 | ||||
| -rw-r--r-- | src/vkvg_context.c | 2 | ||||
| -rw-r--r-- | src/vkvg_fonts.c | 1 | ||||
| -rw-r--r-- | tests/data/text.svg | 57 | ||||
| -rw-r--r-- | tests/vkvg-svg/svg.c | 35 |
6 files changed, 148 insertions, 11 deletions
diff --git a/src/svg/svg-parse.c b/src/svg/svg-parse.c index 1ebdf4c..6f91b55 100644 --- a/src/svg/svg-parse.c +++ b/src/svg/svg-parse.c @@ -114,11 +114,16 @@ //============ //=== TEXT === -#define PREPROC_TEXT vkvg_save(svg->ctx); \ +#define PREPROC_TEXT \ + vkvg_save(svg->ctx); \ parentData = _new_text(); #define PROCESS_TEXT process_element(svg, &attribs, parentData, false); #define POSTPROC_TEXT vkvg_restore(svg->ctx); +#define SVG_ATT_TEXT_X parse_coordinate_list(svg->value, svg->value + svg->value_len, &(CAST(text)->x)); +#define SVG_ATT_TEXT_Y parse_coordinate_list(svg->value, svg->value + svg->value_len, &(CAST(text)->y)); +#define SVG_ATT_TEXT_DX parse_coordinate_list(svg->value, svg->value + svg->value_len, &(CAST(text)->dx)); +#define SVG_ATT_TEXT_DY parse_coordinate_list(svg->value, svg->value + svg->value_len, &(CAST(text)->dy)); //============ //= LINEAR GRADIENT = @@ -134,10 +139,10 @@ if (rg->hasTransform) \ vkvg_pattern_set_matrix(rg->pattern, &rg->transform); \ array_add_SvgElt(&svg->idList, rg); -#define SVG_ATT_LINEARGRADIENT_X1 try_parse_length_or_percentage(svg, &(CAST(linear_gradient)->x1)); -#define SVG_ATT_LINEARGRADIENT_Y1 try_parse_length_or_percentage(svg, &(CAST(linear_gradient)->y1)); -#define SVG_ATT_LINEARGRADIENT_X2 try_parse_length_or_percentage(svg, &(CAST(linear_gradient)->x2)); -#define SVG_ATT_LINEARGRADIENT_Y2 try_parse_length_or_percentage(svg, &(CAST(linear_gradient)->y2)); +#define SVG_ATT_LINEARGRADIENT_X1 try_parse_length_or_percentage(svg, &(CAST(linear_gradient)->x1)); +#define SVG_ATT_LINEARGRADIENT_Y1 try_parse_length_or_percentage(svg, &(CAST(linear_gradient)->y1)); +#define SVG_ATT_LINEARGRADIENT_X2 try_parse_length_or_percentage(svg, &(CAST(linear_gradient)->x2)); +#define SVG_ATT_LINEARGRADIENT_Y2 try_parse_length_or_percentage(svg, &(CAST(linear_gradient)->y2)); #define SVG_ATT_LINEARGRADIENT_GRADIENTUNITS \ if (!strncasecmp ((char*)svg->value, "userspaceonuse", svg->value_len)) \ CAST(linear_gradient)->gradientUnits = svg_gradient_unit_userSpaceOnUse; \ @@ -496,6 +501,18 @@ bool try_parse_floats(const uint8_t **buff_ptr, const uint8_t *const restrict bu *buff_ptr = buff; return true; } +void parse_coordinate_list(const uint8_t *buff, const uint8_t *const restrict buff_end, array_float *arr) { + *arr = array_create_float(10); + float f; + while (buff < buff_end) { + buff = skip_whitespaces (buff, buff_end); + if (!try_parse_float(&buff, buff_end, &f)) + return; + array_add_float(arr, f); + buff = skip_separators(buff, buff_end); + } +} + bool try_parse_flags(const uint8_t **buff_ptr, const uint8_t *const restrict buff_end, int flagCount, ...) { const uint8_t *buff = *buff_ptr; va_list args; @@ -1491,6 +1508,17 @@ void process_element(svg_context *const svg, SvgPresentationAttributes *const at vkvg_close_path(svg->ctx); draw(svg, attribs); } break; + case svg_element_type_text: { + CASTELT(txt, text, elt); + VkvgFont font = NULL; + if (attribs->font_family_len > 0) { + char fam[1024]; + sprintf(fam, "%.*s", attribs->font_family_len, attribs->font_family); + font = vkvg_font_create(svg->dev, fam, attribs->font_size_pt); + vkvg_set_font(svg->ctx, font); + vkvg_font_destroy(font); + } + } break; default: LOG("Unprocessed element type: %d\n", _get_element_type(elt)); return; @@ -1585,6 +1613,22 @@ void process_innerText(SVG_COMMON_SIG, const uint8_t * innerText, size_t txt_len svg_element_type eltType = _get_element_type(parentData); if (eltType == svg_element_type_text) { + CASTELT(txt, text, parentData); + VkvgText tr = vkvg_text_run_create_with_length(svg->ctx, attribs->font_family, attribs->font_family_len); + + if (txt->x.count && txt->y.count) { + vkvg_move_to(svg->ctx, txt->x.elements[0], txt->y.elements[0]); + } + if (attribs->fill_type) { + vkvg_set_opacity(svg->ctx, attribs->opacity * attribs->fill_opacity); + if (attribs->fill_type == svg_paint_type_pattern) + set_pattern(svg, attribs->fill); + else + vkvg_set_source_color(svg->ctx, attribs->fill); + } + + vkvg_show_text_run(svg->ctx, tr); + vkvg_text_run_destroy(tr); LOG("Text innerText: %.*s\n", (int)(buff_end - innerText), innerText); } } diff --git a/src/svg/vkvg_svg_internal.h b/src/svg/vkvg_svg_internal.h index 94d4364..f33fc09 100644 --- a/src/svg/vkvg_svg_internal.h +++ b/src/svg/vkvg_svg_internal.h @@ -113,10 +113,13 @@ typedef struct { svg_unit units; } svg_length_or_percentage; +typedef void (*f_finish) (void*); + typedef struct _svg_element_header{ uint32_t hash; svg_element_type type; uint32_t xlinkHref; // 0 if unused + f_finish finish; // dispose method or null if none. } svg_element_header; typedef struct { @@ -165,8 +168,14 @@ typedef struct { size_t d_len; } svg_element_path; +CTOR_ARRAY(float) + typedef struct { svg_element_header id; + array_float x; + array_float y; + array_float dx; + array_float dy; } svg_element_text; typedef struct { @@ -426,6 +435,7 @@ int try_parse_attibute(svg_context *const svg); bool try_parse_color(const uint8_t **buff_ptr, const uint8_t *const buff_end, svg_paint_type *isEnabled, uint32_t *colorValue); bool try_parse_transform(svg_context *const svg, vkvg_matrix_t *const mat); bool try_parse_length_or_percentage(svg_context *const svg, svg_length_or_percentage *const lop); +void parse_coordinate_list(const uint8_t *buff, const uint8_t *const restrict buff_end, array_float *arr); bool try_parse_viewbox(svg_context *const svg); float parse_opacity(svg_context *const svg); void process_element(svg_context * const svg, SvgPresentationAttributes *const attribs, void *elt, bool use); diff --git a/src/vkvg_context.c b/src/vkvg_context.c index be91447..a2958fd 100644 --- a/src/vkvg_context.c +++ b/src/vkvg_context.c @@ -1250,7 +1250,7 @@ void vkvg_show_text(VkvgContext ctx, const char* text) { } VkvgText vkvg_text_run_create(VkvgContext ctx, const char* text) { - if (vkvg_status(ctx)) + if (vkvg_status(ctx) || vkvg_font_status(ctx->currentFont)) return NULL; VkvgText tr = (vkvg_text_run_t*)calloc(1, sizeof(vkvg_text_run_t)); text_run_init(ctx->currentFont, ctx->textDirection, text, -1, tr); diff --git a/src/vkvg_fonts.c b/src/vkvg_fonts.c index f49e3d3..484b70c 100644 --- a/src/vkvg_fonts.c +++ b/src/vkvg_fonts.c @@ -1074,7 +1074,6 @@ VkvgFont vkvg_font_create_from_file (VkvgDevice dev, const char* fontFilePath, i return _create_font_from_face(dev, face, pointSize, NULL); } VkvgFont vkvg_font_create (VkvgDevice dev, const char* query, float pointSize) { - uint32_t pt26_6 = (uint32_t)(pointSize * 64.f); VkvgFont font = NULL; FontFace face = NULL; diff --git a/tests/data/text.svg b/tests/data/text.svg new file mode 100644 index 0000000..91c2505 --- /dev/null +++ b/tests/data/text.svg @@ -0,0 +1,57 @@ +<svg xmlns="http://w3.org" width="800" height="600" viewBox="0 0 800 600"> + <!-- Visual grid lines for debugging alignments --> + <g stroke="#e0e0e0" stroke-width="1"> + <line x1="50" y1="0" x2="50" y2="600" /> + <line x1="100" y1="0" x2="100" y2="600" /> + <line x1="150" y1="0" x2="150" y2="600" /> + <line x1="200" y1="0" x2="200" y2="600" /> + <line x1="250" y1="0" x2="250" y2="600" /> + <line x1="0" y1="100" x2="800" y2="100" /> + <line x1="0" y1="200" x2="800" y2="200" /> + <line x1="0" y1="300" x2="800" y2="300" /> + </g> + + <!-- Reference point markers --> + <circle cx="50" cy="100" r="3" fill="red" /> + <circle cx="100" cy="100" r="3" fill="red" /> + <circle cx="150" cy="100" r="3" fill="red" /> + <circle cx="200" cy="100" r="3" fill="red" /> + <circle cx="50" cy="200" r="3" fill="blue" /> + <circle cx="50" cy="300" r="3" fill="green" /> + + <!-- TEST 1: Comma/Space Separators & List Exhaustion --> + <!-- 'S' at 50, 'P' at 100, 'A' at 150, 'C' at 200. 'E' and 'S' should rely on default font advances. --> + <text x="50, 100 150,200" y="100" font-family="sans-serif" font-size="24" fill="black"> + SPACES + </text> + + <!-- TEST 2: Absolute Shadowing vs Return-to-Parent --> + <!-- Parent has 3 coordinates. Child has its own 2 coordinates. --> + <!-- 'A' and 'B' use parent (50, 100). --> + <!-- 'C' and 'D' shadow with child (180, 220). --> + <!-- 'E' MUST return to parent's 3rd coordinate (150)! --> + <!-- 'F' has no list items left anywhere, uses standard font advance. --> + <text x="50 100 150" y="200" font-family="sans-serif" font-size="24" fill="darkblue"> + AB<tspan x="180 220" fill="darkorange">CD</tspan>EF + </text> + + <!-- TEST 3: Cumulative Relative Shifts (dx / dy) --> + <!-- Parent starts at absolute (50, 300). --> + <!-- '1' gets pushed +10x, +10y. --> + <!-- '2' gets pushed another +20x, -20y. --> + <!-- '3' has no more list items, stays on normal advance trajectory. --> + <!-- Child '4' shadows with an absolute X override (250) but keeps running on the parent's baseline y trajectory. --> + <text x="50" y="300" dx="10 20" dy="10 -20" font-family="sans-serif" font-size="24" fill="darkgreen"> + 123<tspan x="250" fill="purple">4</tspan> + </text> + + <!-- TEST 4: The Ultimate Chaos Mix --> + <!-- Tests empty space text segments, newlines inside attributes, and severe nested shadowing --> + <text x=" + 50 + 100 + 150 + " y="400" font-family="sans-serif" font-size="24" fill="black"> + XO<tspan dx="15,15" dy="-10,-10" fill="crimson">XX<tspan x="300" dx="0" dy="0" fill="teal">OO</tspan>XX</tspan> + </text> +</svg> diff --git a/tests/vkvg-svg/svg.c b/tests/vkvg-svg/svg.c index e271875..d9e4b59 100644 --- a/tests/vkvg-svg/svg.c +++ b/tests/vkvg-svg/svg.c @@ -1,14 +1,13 @@ #include "test.h" - #include "vkvg-svg.h" static float rotation = 0.f; static const char* path = TESTS_DATA_ROOT "tiger.svg"; static const char* svgSubPath = TESTS_DATA_ROOT "checkbox.svg"; +static const char* svgText = TESTS_DATA_ROOT "text.svg"; void svg_surface() { VkvgSurface svgSurf = vkvg_surface_create_from_svg(device, test_width, test_height, path); - VkvgContext ctx = _initCtx(); vkvg_set_source_rgb(ctx, 1, 1, 1); @@ -46,12 +45,40 @@ void vkvg_svg_sub() { vkvg_destroy(ctx); vkvg_svg_destroy(svg); } +void svg_text() { + VkvgSurface svgSurf = vkvg_surface_create_from_svg(device, test_width, test_height, svgText); + VkvgContext ctx = _initCtx(); + + vkvg_set_source_rgb(ctx, 1, 1, 1); + vkvg_paint(ctx); + vkvg_set_source_surface(ctx, svgSurf, 0, 0); + vkvg_paint(ctx); + + vkvg_destroy(ctx); + vkvg_surface_destroy(svgSurf); +} +void svg_text_frag() { + const char* txt = {"<svg><text id='txt' font-size='50' font-family='sans' x='50' y='50'>The crow jumed on the fox</text></svg>"}; + VkvgSurface svgSurf = vkvg_surface_create_from_svg_fragment(device, test_width, test_height, txt); + VkvgContext ctx = _initCtx(); + + vkvg_set_source_rgb(ctx, 1, 1, 1); + vkvg_paint(ctx); + + vkvg_set_source_surface(ctx, svgSurf, 0, 0); + vkvg_paint(ctx); + + vkvg_destroy(ctx); + vkvg_surface_destroy(svgSurf); +} int main(int argc, char* argv[]) { no_test_size = true; - PERFORM_TEST(svg_surface, argc, argv); + /*PERFORM_TEST(svg_surface, argc, argv); PERFORM_TEST(vkvg_svg, argc, argv); - PERFORM_TEST(vkvg_svg_sub, argc, argv); + PERFORM_TEST(vkvg_svg_sub, argc, argv);*/ + PERFORM_TEST(svg_text_frag, argc, argv); + //PERFORM_TEST(svg_text, argc, argv); return 0; } |