summaryrefslogtreecommitdiffstatsci
diff options
context:
space:
mode:
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>2026-10-13 19:40:44 +0200
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>2026-10-13 19:40:44 +0200
commit35e098cb8764eb5dc9f61a3abac67f971a259a9e (patch)
treecc8153411079a59f8bcb601d15d10ce4ac227ba6
parentaa9296a0b81c1cc72d42d5367004d7144766f924 (diff)
<text> support wip
-rw-r--r--src/svg/svg-parse.c54
-rw-r--r--src/svg/vkvg_svg_internal.h10
-rw-r--r--src/vkvg_context.c2
-rw-r--r--src/vkvg_fonts.c1
-rw-r--r--tests/data/text.svg57
-rw-r--r--tests/vkvg-svg/svg.c35
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;
}