From: yu.dongliang <18588496441@163.com> Date: Thu, 30 Apr 2026 08:57:37 +0000 (+0800) Subject: html/css: auto split a long UTF-8 text to next line, Chinese (中文) ok X-Git-Url: http://baseworks.info/?a=commitdiff_plain;h=bb110687abca2c1a66dabcf9b0a4fcfa9e82c6e1;p=abc.git html/css: auto split a long UTF-8 text to next line, Chinese (中文) ok --- diff --git a/examples/p_multi_line.html b/examples/p_multi_line.html new file mode 100644 index 0000000..b90bf63 --- /dev/null +++ b/examples/p_multi_line.html @@ -0,0 +1,18 @@ + + + + + + + +

A drawing operator that generates the shape from a string of UTF-8 characters, rendered according to the current font_face, font_size (font_matrix), and font_options.This function first computes a set of glyphs for the string of text. The first glyph is placed so that its origin is at the current point. The origin of each subsequent glyph is offset from that of the previous glyph by the advance values of the previous glyph.

+

一旦为页面设置了恰当的 DTD,大多数浏览器都会按照上面的图示来呈现内容。然而 IE 5 和 6 的呈现却是不正确的。根据 W3C 的规范,元素内容占据的空间是由 width 属性设置的,而内容周围的 padding 和 border 值是另外计算的。不幸的是,IE5.X 和 6 在怪异模式中使用自己的非标准模型。这些浏览器的 width 属性不是内容的宽度,而是内容、内边距和边框的宽度的总和。

+ + diff --git a/examples/table.html b/examples/table.html index 15b3ba1..7f2474b 100644 --- a/examples/table.html +++ b/examples/table.html @@ -7,11 +7,7 @@ table,th,td border:1px solid red; } -table -{ - width:100%; -/* border-collapse:collapse;*/ -} +table {width:100%;} th { diff --git a/html/abc_html.c b/html/abc_html.c index af65462..ebfdcb7 100644 --- a/html/abc_html.c +++ b/html/abc_html.c @@ -263,6 +263,9 @@ static html_attr_t p_attrs[] = {border_keys, "", ABC_HTML_ATTR_BORDER, ABC_HTML_FLAG_SHOW}, {padding_keys, "", ABC_HTML_ATTR_PADDING, ABC_HTML_FLAG_SHOW}, + {width_keys, "", ABC_HTML_ATTR_WIDTH, ABC_HTML_FLAG_SHOW}, + {height_keys, "", ABC_HTML_ATTR_HEIGHT, ABC_HTML_FLAG_SHOW}, + {class_keys, "", ABC_HTML_ATTR_CLASS, ABC_HTML_FLAG_SHOW}, {style_keys, "", ABC_HTML_ATTR_STYLE, ABC_HTML_FLAG_SHOW}, {id_keys, "", ABC_HTML_ATTR_ID, ABC_HTML_FLAG_SHOW}, diff --git a/html/abc_io_http.c b/html/abc_io_http.c index 18a232c..992bfed 100644 --- a/html/abc_io_http.c +++ b/html/abc_io_http.c @@ -491,7 +491,7 @@ static int __http_popc(abc_io_t* io) assert(http->rpos > 0); if (http->rpos < http->rbuf->len) - c = http->rbuf->data[http->rpos++]; + c = (uint8_t)http->rbuf->data[http->rpos++]; else c = EOF; pthread_mutex_unlock(&http->mutex); diff --git a/html/abc_io_str.c b/html/abc_io_str.c index ba31fcf..8dedd95 100644 --- a/html/abc_io_str.c +++ b/html/abc_io_str.c @@ -47,8 +47,10 @@ static int __str_popc(abc_io_t* io) if (io && io->priv) { s = io->priv; - if (s->text_i < s->text->len) - return s->text->data[s->text_i++]; + if (s->text_i < s->text->len) { + uint8_t c = s->text->data[s->text_i++]; + return c; + } } return EOF; diff --git a/html/abc_obj.h b/html/abc_obj.h index ee84845..9c639e2 100644 --- a/html/abc_obj.h +++ b/html/abc_obj.h @@ -7,6 +7,7 @@ #include"abc_io.h" typedef struct abc_obj_s abc_obj_t; +typedef struct abc_text_s abc_text_t; enum abc_objs { @@ -151,6 +152,22 @@ enum abc_border_style ABC_BORDER_DASHED, }; +enum abc_line_style +{ + ABC_LINE_NONE, + ABC_LINE_UNDER, + ABC_LINE_OVER, + ABC_LINE_THROUGH, +}; + +struct abc_text_s +{ + abc_text_t* next; + + int start; + int len; +}; + struct abc_obj_s { scf_list_t list; @@ -192,6 +209,7 @@ struct abc_obj_s char** keys; scf_string_t* value; scf_string_t* text; + abc_text_t* text_splits; // for layout, split a long text to multi-lines and save every-line here int text_line; int text_pos; diff --git a/ui/abc_layout_h1.c b/ui/abc_layout_h1.c index 2e869ab..c735cdc 100644 --- a/ui/abc_layout_h1.c +++ b/ui/abc_layout_h1.c @@ -1,5 +1,116 @@ #include"abc.h" +static int __layout_text_split(cairo_text_extents_t* extents, cairo_t* cr, const uint8_t* text, int len) +{ + int i; + for (i = len; i > 0; i--) { + int c = text[i]; + + if ('\0' == c || ' ' == c || '\t' == c || '\r' == c || '\n' == c || '-' == c) + break; + else if (c < 0x80) + continue; + + if ((c >> 6) != 0x2) + break; + } + + if (i > 0) + len = i; + + scf_string_t* s = scf_string_cstr_len(text, len); + if (!s) + return -ENOMEM; + cairo_text_extents(cr, s->data, extents); + + scf_string_free(s); + return len; +} + +int __layout_text(cairo_text_extents_t* extents, cairo_t* cr, abc_obj_t* obj, int width) +{ + abc_obj_t* attr; + abc_obj_t* style; + + attr = abc_obj_find_attr(obj, ABC_HTML_ATTR_FONT); + if (attr) { + int bold = CAIRO_FONT_WEIGHT_NORMAL; + int italic = CAIRO_FONT_SLANT_NORMAL; + + style = abc_obj_find_attr(obj, ABC_HTML_ATTR_FONT_STYLE); + if (style) { + if (!__html_strcmp(style->value->data, "bold")) + bold = CAIRO_FONT_WEIGHT_BOLD; + + else if (!__html_strcmp(style->value->data, "italic") || !__html_strcmp(style->value->data, "oblique")) + italic = CAIRO_FONT_SLANT_OBLIQUE; + } + + cairo_select_font_face(cr, attr->value->data, italic, bold); + } + + int size = 16; + attr = abc_obj_find_attr(obj, ABC_HTML_ATTR_FONT_SIZE); + if (attr) + size = atoi(attr->value->data); + + cairo_set_font_size(cr, size); + cairo_text_extents (cr, obj->text->data, extents); + + int w = extents->x_bearing + extents->width; + if (w <= width) + return 0; + + extents->width = width; + extents->height = 0; + + if (obj->text_splits) + scf_slist_clear(obj->text_splits, abc_text_t, next, free); + + assert(NULL == obj->text_splits); + abc_text_t** h = &obj->text_splits; + + int n_lines = w / width; + int n_chars = obj->text->len * 5 / 4 / n_lines; + int n; + int i; + + for (i = 0; i < obj->text->len; ) { + n = n_chars; + + if (n > obj->text->len - i) + n = obj->text->len - i; + + while (1) { + cairo_text_extents_t tmp; + + n = __layout_text_split(&tmp, cr, obj->text->data + i, n); + if (n < 0) + return n; + + if (tmp.x_bearing + tmp.width <= width) + { + abc_text_t* t = calloc(1, sizeof(abc_text_t)); + if (!t) + return -ENOMEM; + t->start = i; + t->len = n; + + *h = t; + h = &t->next; + i += n; + + extents->height += tmp.height; + break; + } + + n--; + } + } + + return 0; +} + int abc_layout_h1(abc_layout_t* layout, abc_obj_t* obj, int width, int height) { abc_obj_t* attr; @@ -34,21 +145,15 @@ int abc_layout_h1(abc_layout_t* layout, abc_obj_t* obj, int width, int height) int size = 16; attr = abc_obj_find_attr(obj, ABC_HTML_ATTR_FONT_SIZE); - if (attr) { + if (attr) size = atoi(attr->value->data); - cairo_set_font_size(cr, size); - } - - cairo_font_options_t *options = cairo_font_options_create(); - cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_SUBPIXEL); - cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_FULL); - cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_ON); - cairo_set_font_options(cr, options); - cairo_font_options_destroy(options); + cairo_set_font_size (cr, size); cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); - cairo_text_extents(cr, obj->text->data, &extents); + int ret = __layout_text(&extents, cr, obj, width); + if (ret < 0) + scf_loge("ret: %d\n", ret); int w = 0; if (ABC_HTML_OL == obj->parent->type) @@ -80,7 +185,7 @@ int abc_layout_h1(abc_layout_t* layout, abc_obj_t* obj, int width, int height) obj->w = (obj->w + 3) & ~0x3; obj->h = (obj->h + 3) & ~0x3; - scf_logd("%s, w: %d, h: %d, x_bearing: %lg, y_bearing: %lg, width: %lg, height: %lg, x_advance: %lg, y_advance: %lg\n", + scf_logi("%s, w: %d, h: %d, x_bearing: %lg, y_bearing: %lg, width: %lg, height: %lg, x_advance: %lg, y_advance: %lg\n", obj->text->data, obj->w, obj->h, extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance, extents.y_advance); diff --git a/ui/abc_layout_td.c b/ui/abc_layout_td.c index fac5fff..82327ff 100644 --- a/ui/abc_layout_td.c +++ b/ui/abc_layout_td.c @@ -37,7 +37,7 @@ int abc_layout_td(abc_layout_t* layout, abc_obj_t* obj, int width, int height) while (table && ABC_HTML_TABLE != table->type) table = table->parent; - // width of event table element is width of the max one + // width of every table element is width of the max one if (obj->w < table->td_width) obj->w = table->td_width; else diff --git a/ui/abc_render_h1.c b/ui/abc_render_h1.c index 461f259..d4b6a50 100644 --- a/ui/abc_render_h1.c +++ b/ui/abc_render_h1.c @@ -38,6 +38,42 @@ static int _render_fini_h1(abc_render_t* render) return 0; } +void __draw_text(cairo_text_extents_t* extents, cairo_t* cr, const char* text, double x, double y, int line_type, int line_width) +{ + cairo_text_extents(cr, text, extents); + double w = extents->width; + + x += extents->x_bearing; + y -= extents->y_bearing; + + cairo_set_line_width(cr, 1); + cairo_move_to(cr, x, y); + cairo_show_text(cr, text); + cairo_stroke(cr); + + cairo_set_line_width(cr, line_width); + switch (line_type) + { + case ABC_LINE_UNDER: + cairo_move_to(cr, x, y + line_width + 1.0); + cairo_line_to(cr, w, y + line_width + 1.0); + break; + + case ABC_LINE_OVER: + cairo_move_to(cr, x, 1.0); + cairo_line_to(cr, w, 1.0); + break; + + case ABC_LINE_THROUGH: + cairo_move_to(cr, x, y + extents->y_bearing / 2.0 + line_width); + cairo_line_to(cr, w, y + extents->y_bearing / 2.0 + line_width); + break; + default: + break; + }; + cairo_stroke(cr); +} + int __init_text(cairo_t* cr, abc_obj_t* obj, int num_flag) { abc_obj_t* attr = abc_obj_find_attr(obj, ABC_HTML_ATTR_FONT); @@ -64,59 +100,68 @@ int __init_text(cairo_t* cr, abc_obj_t* obj, int num_flag) double size = 16.0; attr = abc_obj_find_attr(obj, ABC_HTML_ATTR_FONT_SIZE); - if (attr) { + if (attr) size = atoi(attr->value->data); - cairo_set_font_size(cr, size); - } - - cairo_font_options_t *options = cairo_font_options_create(); - cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_SUBPIXEL); - cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_FULL); - cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_ON); - cairo_set_font_options(cr, options); - cairo_font_options_destroy(options); attr = abc_obj_find_attr(obj, ABC_HTML_ATTR_FONT_COLOR); if (attr) abc_css_color(&r, &g, &b, attr->value->data); + cairo_set_font_size (cr, size); cairo_set_source_rgba(cr, r, g, b, 1.0); - cairo_text_extents (cr, obj->text->data, &extents); - switch (obj->parent->type) + if (ABC_HTML_OL == obj->parent->type && num_flag) { - case ABC_HTML_OL: - if (num_flag) - cairo_move_to(cr, obj->parent->padding - (extents.x_bearing + extents.width + size / 2), -obj->y_bearing); - else - cairo_move_to(cr, extents.x_bearing + obj->parent->padding, -extents.y_bearing); - break; - default: - cairo_move_to(cr, extents.x_bearing + obj->parent->padding, -extents.y_bearing); - break; - }; + cairo_text_extents(cr, obj->text->data, &extents); - cairo_show_text(cr, obj->text->data); - cairo_stroke(cr); + cairo_move_to (cr, obj->parent->padding - (extents.x_bearing + extents.width + size / 2), -obj->y_bearing); + cairo_show_text(cr, obj->text->data); + cairo_stroke(cr); + return 0; + } + + double line_width = 0.5 + size / 16.0; + int line_type = ABC_LINE_NONE; attr = abc_obj_find_attr(obj, ABC_HTML_ATTR_TEXT_DECORATION); if (attr) { - double line = 0.5 + size / 16.0; - cairo_set_line_width(cr, line); + if (!__html_strcmp(attr->value->data, "underline")) + line_type = ABC_LINE_UNDER; - if (!__html_strcmp(attr->value->data, "underline")) { - cairo_move_to(cr, extents.x_bearing, -extents.y_bearing + line + 1.0); - cairo_line_to(cr, extents.width, -extents.y_bearing + line + 1.0); + else if (!__html_strcmp(attr->value->data, "overline")) + line_type = ABC_LINE_OVER; - } else if (!__html_strcmp(attr->value->data, "overline")) { - cairo_move_to(cr, extents.x_bearing, 1.0); - cairo_line_to(cr, extents.width, 1.0); + else if (!__html_strcmp(attr->value->data, "line-through")) + line_type = ABC_LINE_THROUGH; + } + + if (!obj->text_splits) { + __draw_text(&extents, cr, obj->text->data, obj->parent->padding, 0.0, line_type, line_width); + return 0; + } + + scf_string_t* s; + abc_text_t* t; + + double x = obj->parent->padding; + double y = 0.0; - } else if (!__html_strcmp(attr->value->data, "line-through")) { - cairo_move_to(cr, extents.x_bearing, -extents.y_bearing / 2.0 + line); - cairo_line_to(cr, extents.width, -extents.y_bearing / 2.0 + line); + while (obj->text_splits) { + t = obj->text_splits; + + s = scf_string_cstr_len(obj->text->data + t->start, t->len); + if (!s) { + scf_slist_clear(obj->text_splits, abc_text_t, next, free); + return -ENOMEM; } - cairo_stroke(cr); + + __draw_text(&extents, cr, s->data, x, y, line_type, line_width); + y += extents.height; + + obj->text_splits = t->next; + + scf_string_free(s); + free(t); } return 0;