From 24500f52a3bdbe9a2a033dcaa300c54ae3321720 Mon Sep 17 00:00:00 2001 From: Alec Murphy Date: Wed, 16 Apr 2025 14:53:16 -0400 Subject: [PATCH] System/Libraries/(Css,Graphics2D,Html): Support underlined text This commit adds the necessary functions to minimally implement support for CSS text-decoration: underline. --- System/Libraries/Css/Tokenizer.HC | 2 ++ System/Libraries/Graphics2D.HC | 38 ++++++++++++++++++++++++++++++ System/Libraries/Html/Renderer.HC | 23 ++++++++++++++++++ System/Libraries/Html/Tokenizer.HC | 1 + 4 files changed, 64 insertions(+) diff --git a/System/Libraries/Css/Tokenizer.HC b/System/Libraries/Css/Tokenizer.HC index 6bf157b..707acc3 100644 --- a/System/Libraries/Css/Tokenizer.HC +++ b/System/Libraries/Css/Tokenizer.HC @@ -1,6 +1,8 @@ #define CSS_TEXT_ALIGN_CENTER 1 #define CSS_TEXT_ALIGN_RIGHT 2 +#define CSS_TEXT_UNDERLINE 1 + #define CSS_TOKENIZER_STATE_CONSUME_MATCH 0 #define CSS_TOKENIZER_STATE_CONSUME_PROPERTY 1 #define CSS_TOKENIZER_STATE_CONSUME_VALUE 2 diff --git a/System/Libraries/Graphics2D.HC b/System/Libraries/Graphics2D.HC index 9681345..655b79f 100644 --- a/System/Libraries/Graphics2D.HC +++ b/System/Libraries/Graphics2D.HC @@ -1162,6 +1162,24 @@ I64 X2Pos(CDC* src) return -1; } +I64 Y2Pos(CDC* src) +{ + I64 x = src->width - 1; + I64 y = src->height - 1; + I64 color; + while (y > -1) { + x = src->width - 1; + while (x > -1) { + color = GrPeek(src, x, y); + if (color) + return y; + x--; + } + y--; + } + return -1; +} + I64 @get_truetype_text_width(U8* font_name, I64 size, U8* text) { stbtt_fontinfo* font = Fonts->@(font_name); @@ -1178,6 +1196,26 @@ I64 @get_truetype_text_width(U8* font_name, I64 size, U8* text) return res; } +I64 @get_truetype_baseline(U8* font_name, I64 size) +{ + stbtt_fontinfo* font = Fonts->@(font_name); + if (!font) { + return 0; + } + I64 res = 0; + CDC* dc = DCNew(Display.Width() / 2, (size * 2)); + Free(dc->body); + I32 i32char[16]; + MemSet(&i32char, NULL, 16); + i32char[0] = 'o'; + dc->body = @stbtt_RenderText(font, dc->width_internal, dc->height, ToI64(size * 1.2), &i32char); + dc->width -= 16; + dc->height -= size / 4; + res = Y2Pos(dc); + DCDel(dc); + return res; +} + U0 Text2D(Context2D* ctx, U8* font_name, I64 x, I64 y, I64 size, U32 color, U8* text) { stbtt_fontinfo* font = Fonts->@(font_name); diff --git a/System/Libraries/Html/Renderer.HC b/System/Libraries/Html/Renderer.HC index 9045727..34bca8e 100644 --- a/System/Libraries/Html/Renderer.HC +++ b/System/Libraries/Html/Renderer.HC @@ -308,6 +308,7 @@ Bool @apply_css_rules_to_node(@html_dom_node* node, HtmlRenderer* renderer) node->fontSize = node->parentNode->fontSize; node->fontWeight = node->parentNode->fontWeight; node->textAlign = node->parentNode->textAlign; + node->textDecoration = node->parentNode->textDecoration; node->italic = node->parentNode->italic; } @@ -425,6 +426,15 @@ Bool @apply_css_rules_to_node(@html_dom_node* node, HtmlRenderer* renderer) if (!StrICmp(key->name, "text-align") && !StrICmp(values->@(0), "right")) node->textAlign = CSS_TEXT_ALIGN_RIGHT; + if (!StrICmp(key->name, "text-decoration")) { + if (!StrICmp(values->@(0), "none")) { + node->textDecoration = 0; + } + if (!StrICmp(values->@(0), "underline")) { + node->textDecoration = CSS_TEXT_UNDERLINE; + } + } + if (!StrICmp(key->name, "line-height") && !StrICmp(values->@(0) + StrLen(values->@(0)) - 2, "px")) { StrCpy(node_tmpnum_buf, values->@(0)); node_tmpnum_buf[StrLen(node_tmpnum_buf) - 2] = NULL; @@ -849,6 +859,7 @@ U0 @render_node_text(@html_dom_node* node, HtmlRenderer* renderer) U32 fragment_bounding_box_color = Color(0x00, 0xff, 0x00); U8* font_name = @resolved_font_name_for_node(node->parentNode); + I64 underline_y_pos = -1; for (i = 0; i < fragment_count; i++) { if (fragments[i] && *fragments[i]) { @@ -860,6 +871,18 @@ U0 @render_node_text(@html_dom_node* node, HtmlRenderer* renderer) fragment_widget->data = node; fragment_widget->ctx = NewContext2D(text_width, ToI64(node->parentNode->fontSize * 1.2))->fill(node->parentNode->backgroundColor)->text(font_name, 0, 0, node->parentNode->fontSize, node->parentNode->color, fragments[i]); + switch (node->parentNode->textDecoration) { + case CSS_TEXT_UNDERLINE: + if (underline_y_pos < 0) + underline_y_pos = @get_truetype_baseline(font_name, node->parentNode->fontSize) + 2; + if (!(underline_y_pos < 0)) { + fragment_widget->ctx->line(0, underline_y_pos, fragment_widget->ctx->width, underline_y_pos, node->parentNode->color); + } + break; + default: + break; + } + if (renderer->debug && fragment_widget->ctx) { fragment_widget->ctx->line(0, 0, fragment_widget->ctx->width - 1, 0, fragment_bounding_box_color); fragment_widget->ctx->line(0, fragment_widget->ctx->height - 1, fragment_widget->ctx->width - 1, fragment_widget->ctx->height - 1, fragment_bounding_box_color); diff --git a/System/Libraries/Html/Tokenizer.HC b/System/Libraries/Html/Tokenizer.HC index 39ee371..99d6fdf 100644 --- a/System/Libraries/Html/Tokenizer.HC +++ b/System/Libraries/Html/Tokenizer.HC @@ -97,6 +97,7 @@ class @html_dom_node : JsonElement U8* fontFamily; I64 fontSize; I64 fontWeight; + I64 textDecoration; Bool italic; Bool display_block; };