From: yu.dongliang <18588496441@163.com> Date: Wed, 13 May 2026 10:58:36 +0000 (+0800) Subject: css: support simple combinators with pse class or pse element X-Git-Url: http://baseworks.info/?a=commitdiff_plain;h=9a94310b226854dd2ae542a74f60c6c1c85b8a3f;p=abc.git css: support simple combinators with pse class or pse element --- diff --git a/examples/div.html b/examples/div.html new file mode 100644 index 0000000..bf86a04 --- /dev/null +++ b/examples/div.html @@ -0,0 +1,22 @@ + + + + + + + +

盒子模型演示

+ +

CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距,边框,填充,和实际内容。

+ +
这里是盒子内的实际内容。有 25px 内间距,25px 外间距、25px 绿色边框。
+ + diff --git a/examples/p_group.html b/examples/p_group.html new file mode 100644 index 0000000..535de1d --- /dev/null +++ b/examples/p_group.html @@ -0,0 +1,33 @@ + + + + +菜鸟教程(runoob.com) + + + + +

这个段落是蓝色文本,居中对齐。

+
+

这个段落不是蓝色文本。

+
+

所有 class="marked"元素内的 p 元素指定一个样式,但有不同的文本颜色。

+ +

带下划线的 p 段落。

+ + diff --git a/examples/p_pse_class.html b/examples/p_pse_class.html new file mode 100644 index 0000000..2e1e5ea --- /dev/null +++ b/examples/p_pse_class.html @@ -0,0 +1,23 @@ + + + + + + + + +

I am a strong man. I am a strong man.

+

I am a strong man. I am a strong man.

+

注意: 当 :first-child 作用于 IE8 以及更早版本的浏览器, !DOCTYPE 必须已经定义.

+ + diff --git a/html/abc_css.c b/html/abc_css.c index fe9ca42..cb71c3d 100644 --- a/html/abc_css.c +++ b/html/abc_css.c @@ -8,7 +8,7 @@ static int __css_drop_comment(abc_obj_t* css) { abc_char_t* c = __io_pop_char(&css->io); if (!c) - return -1; + return -EINVAL; if ('*' != c->c) { __io_push_char(&css->io, c); @@ -23,14 +23,14 @@ static int __css_drop_comment(abc_obj_t* css) while (1) { c = __io_pop_char(&css->io); if (!c) - return -1; + return -EINVAL; int tmp = c->c; free(c); c = NULL; if (EOF == tmp) - return -1; + return -EINVAL; if ('\n' == tmp) { css->text_line++; @@ -48,22 +48,22 @@ static int __css_drop_comment(abc_obj_t* css) return 0; } -static int __css_parse_value(abc_obj_t* css, abc_obj_t* attr) +static int __css_end_value(int c) { - scf_string_t* value = scf_string_alloc(); - if (!value) - return -ENOMEM; + if (';' == c || '}' == c || EOF == c) + return 1; + return 0; +} +static int __css_parse_str(abc_obj_t* css, scf_string_t* s, int (*end)(int c)) +{ abc_char_t* c = NULL; - abc_char_t* c2 = NULL; int c0 = ' '; while (1) { c = __io_pop_char(&css->io); - if (!c) { - scf_string_free(value); - return -1; - } + if (!c) + return -EINVAL; css->text_pos += c->len; @@ -76,111 +76,111 @@ static int __css_parse_value(abc_obj_t* css, abc_obj_t* attr) if (ret < 0) { free(c); - scf_string_free(value); return ret; } } - if ('\n' == c->c) { - css->text_line++; - css->text_pos = 0; - } - - if (';' == c->c || '}' == c->c || EOF == c->c) + if (end(c->c)) break; - if (' ' == c0) { - if (' ' == c->c || '\t' == c->c || '\r' == c->c || '\n' == c->c) { - free(c); - continue; - } + int ret = 0; + switch (c->c) { // drop more ' ' & add c to string + case '\n': + css->text_line++; + css->text_pos = 0; + case '\t': + case '\r': + case ' ': + if (' ' == c0 + || '>' == c0 || '+' == c0 || '~' == c0 + || ',' == c0 || ':' == c0 || '.' == c0 || '#' == c0) + break; - c0 = c->c; - } + ret = scf_string_cat_cstr_len(s, " ", 1); + c0 = ' '; + break; - scf_string_cat_cstr_len(value, c->utf8, c->len); + case '>': + case '+': + case '~': + case ',': + case ':': + case '.': +// case '#': + if (s->len > 0 && ' ' == s->data[s->len - 1]) { + s->data[s->len - 1] = c->c; + c0 = c->c; + break; + } + default: + ret = scf_string_cat_cstr_len(s, c->utf8, c->len); + c0 = c->c; + break; + }; free(c); c = NULL; + if (ret < 0) + return ret; } + while (s->len > 0 && ' ' == s->data[s->len - 1]) + s->data[--s->len] = '\0'; + int tmp = c->c; free(c); - c = NULL; - - if (attr->value) - scf_string_free(attr->value); - - attr->value = value; return tmp; } -static int __css_parse_attr2(abc_obj_t* css, abc_obj_t* obj, const html_attr_t* attrs, int n_attrs) +static int __css_parse_value(abc_obj_t* css, abc_obj_t* attr) { - scf_string_t* key = scf_string_alloc(); - if (!key) + scf_string_t* value = scf_string_alloc(); + if (!value) return -ENOMEM; - abc_char_t* c = NULL; - - while (1) { - c = __io_pop_char(&css->io); - if (!c) { - scf_string_free(key); - return -1; - } + int tmp = __css_parse_str(css, value, __css_end_value); - css->text_pos += c->len; - - if ('}' == c->c || EOF == c->c) { - free(c); - scf_string_free(key); - return '}'; - } + switch (tmp) { + case ';': + case '}': + case EOF: + if (attr->value) + scf_string_free(attr->value); - if (':' == c->c) + attr->value = value; break; + default: + scf_string_free(value); + break; + }; - if ('/' == c->c) { - int ret = __css_drop_comment(css); - if (0 == ret) { - free(c); - continue; - } + return tmp; +} - if (ret < 0) { - free(c); - scf_string_free(key); - return ret; - } - } +static int __css_end_attr(int c) +{ + if (':' == c || ';' == c || '}' == c || EOF == c) + return 1; + return 0; +} - if ('_' == c->c - || '-' == c->c - || ('a' <= c->c && 'z' >= c->c)) { - scf_string_cat_cstr_len(key, c->utf8, c->len); +static int __css_parse_attr2(abc_obj_t* css, abc_obj_t* obj, const html_attr_t* attrs, int n_attrs) +{ + scf_string_t* key = scf_string_alloc(); + if (!key) + return -ENOMEM; - } else if (' ' == c->c || '\t' == c->c || '\r' == c->c) { + int tmp = __css_parse_str(css, key, __css_end_attr); - } else if ('\n' == c->c) { - css->text_line++; - css->text_pos = 0; - } else { - scf_loge("invalid char '%c:%#x' in CSS attribute, file: %s, line: %d\n", c->c, c->c, css->file->data, css->text_line); - free(c); + switch (tmp) { + case ':': + break; + default: scf_string_free(key); - return -1; - } - - free(c); - c = NULL; - } - - int tmp = c->c; - - free(c); - c = NULL; + return tmp; + break; + }; scf_list_t* l; abc_obj_t* attr; @@ -199,7 +199,7 @@ static int __css_parse_attr2(abc_obj_t* css, abc_obj_t* obj, const html_attr_t* scf_loge("invalid HTML attribute '%s' in file: %s, line: %d\n", key->data, css->file->data, css->text_pos); scf_string_free(key); - return -1; + return -EINVAL; found: for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) { @@ -235,113 +235,234 @@ static int __css_parse_attr(abc_obj_t* css, abc_obj_t* obj, const html_attr_t* a while (1) { ret = __css_parse_attr2(css, obj, attrs, n_attrs); - if (ret < 0) - return ret; if ('}' == ret || EOF == ret) break; + + if (ret < 0) + return ret; } return ret; } -static int __css_parse_obj(abc_obj_t* css, abc_char_t* c) +static int __css_parse_comma(abc_obj_t* css, abc_obj_t* obj) { html_label_t* label; - abc_obj_t* obj; - scf_string_t* key = scf_string_cstr_len(c->utf8, c->len); + scf_list_t h; - css->text_pos += c->len; - free(c); - c = NULL; + scf_list_init(&h); - if (!key) - return -ENOMEM; + int combinator = -1; + int id = 0; + int class = 0; + int pseClass = -1; + int pseElement = -1; + int c; + int i; + int j = -1; + int k = -1; - int ret; - int dot = -1; - int colon = -1; - int comma = -1; + for (i = obj->text->len - 1; i >= 0; i--) { + c = obj->text->data[i]; - while (1) { - c = __io_pop_char(&css->io); - if (!c) { - scf_string_free(key); - return -1; + assert('\t' != c); + assert('\r' != c); + assert('\n' != c); + + if (' ' == c) { + if (combinator < 0 && k >= 0) + combinator = i; + continue; } - css->text_pos += c->len; + if (',' == c) { + if (k < 0) + continue; - if ('{' == c->c) - break; + assert(j >= 0); - if ('/' == c->c) { - ret = __css_drop_comment(css); - if (0 == ret) { - free(c); - continue; + scf_string_t* key = scf_string_cstr_len(obj->text->data + j, k + 1 - j); + if (!key) { + scf_list_clear(&h, abc_obj_t, list, abc_obj_free); + return -ENOMEM; } - if (ret < 0) { - free(c); + if (id > 0) + label = __html_find_label2(ABC_CSS_ID); + else if (class > 0) + label = __html_find_label2(ABC_CSS_CLASS); + + else if (pseClass > 0) + label = __html_find_label2(ABC_CSS_PSE_CLASS); + else if (pseElement > 0) + label = __html_find_label2(ABC_CSS_PSE_ELEMENT); + + else if (j < combinator && combinator < k) + label = __html_find_label2(ABC_CSS_COMBINATOR); + else { + label = __html_find_label(key->data); + if (!label) { + scf_loge("css selector '%s' invalid, file: %s, line: %d, pos: %d\n", key->data, obj->file->data, obj->line, j); + + scf_string_free(key); + scf_list_clear(&h, abc_obj_t, list, abc_obj_free); + return -EINVAL; + } + } + + abc_obj_t* rule = abc_obj_alloc(obj->file, obj->line, j, label->type); + if (!rule) { + scf_string_free(key); + scf_list_clear(&h, abc_obj_t, list, abc_obj_free); + return -ENOMEM; + } + + rule->flags = label->flags; + rule->keys = label->names; + rule->parent = css; + + if (rule->type < ABC_HTML_NB) scf_string_free(key); + else + rule->text = key; + key = NULL; + + int ret = abc_obj_copy_attrs(rule, obj); + if (ret < 0) { + abc_obj_free(rule); + scf_list_clear(&h, abc_obj_t, list, abc_obj_free); return ret; } + + scf_list_add_front(&h, &rule->list); + + combinator = -1; + id = 0; + class = 0; + pseClass = -1; + pseElement = -1; + + j = -1; + k = -1; + continue; } - if (' ' == c->c || '\t' == c->c || '\r' == c->c) { + j = i; + if (k < 0) + k = i; - } else if ('\n' == c->c) { - css->text_line++; - css->text_pos = 0; - } else { - if (dot < 0 && '.' == c->c) - dot = key->len; + switch (c) { + case '#': + id++; + break; + case '.': + class++; + break; - if (colon < 0 && ':' == c->c) - colon = key->len; + case ':': + if (pseClass < 0) + pseClass = i; + else if (pseClass - 1 == i) { + pseElement = i; + pseClass = -1; + } + break; + + case '>': + case '+': + case '~': + if (combinator < 0) + combinator = i; + break; + default: + break; + }; + } + + if (k >= 0) { + assert(k < obj->text->len); + + obj->text->data[k + 1] = '\0'; + obj->text->len = k + 1; + + if (id > 0) + label = __html_find_label2(ABC_CSS_ID); + else if (class > 0) + label = __html_find_label2(ABC_CSS_CLASS); - if (comma < 0 && ',' == c->c) - comma = key->len; + else if (pseClass > 0) + label = __html_find_label2(ABC_CSS_PSE_CLASS); + else if (pseElement > 0) + label = __html_find_label2(ABC_CSS_PSE_ELEMENT); - scf_string_cat_cstr_len(key, c->utf8, c->len); + else if (0 < combinator && combinator < k) + label = __html_find_label2(ABC_CSS_COMBINATOR); + else { + label = __html_find_label(obj->text->data); + if (!label) { + scf_loge("css selector '%s' invalid, file: %s, line: %d, pos: %d\n", obj->text->data, obj->file->data, obj->line, k); + + scf_list_clear(&h, abc_obj_t, list, abc_obj_free); + return -EINVAL; + } } - free(c); - c = NULL; + obj->type = label->type; + obj->flags = label->flags; + obj->keys = label->names; + obj->parent = css; + + if (obj->type < ABC_HTML_NB) { + scf_string_free(obj->text); + obj->text = NULL; + } + + scf_list_add_front(&h, &obj->list); + + scf_list_mov2(&css->childs, &h); + return 0; } - int tmp = c->c; + scf_list_clear(&h, abc_obj_t, list, abc_obj_free); + return -EINVAL; +} + +static int __css_end_obj(int c) +{ + if ('{' == c || EOF == c) + return 1; + return 0; +} + +static int __css_parse_obj(abc_obj_t* css, abc_char_t* c) +{ + html_label_t* label; + abc_obj_t* obj; + scf_string_t* key = scf_string_cstr_len(c->utf8, c->len); + + css->text_pos += c->len; free(c); c = NULL; - switch (key->data[0]) - { - case '#': - dot = 0; - label = __html_find_label2(ABC_CSS_ID); - break; + if (!key) + return -ENOMEM; - case '.': - dot = 0; - label = __html_find_label2(ABC_CSS_CLASS); + int tmp = __css_parse_str(css, key, __css_end_obj); + switch (tmp) { + case '{': break; default: - if (dot >= 0) - label = __html_find_label2(ABC_CSS_CLASS); - else if (comma >= 0) - label = __html_find_label2(ABC_CSS_COMMA); - else if (colon >= 0) - label = __html_find_label(key->data + colon); - else - label = __html_find_label(key->data); + scf_string_free(key); + return -EINVAL; break; }; + label = __html_find_label2(ABC_CSS_COMBINATOR); if (!label) { scf_loge("invalid HTML label '%s' in file: %s, line: %d\n", key->data, css->file->data, css->text_line); scf_string_free(key); - return -1; + return -EINVAL; } obj = abc_obj_alloc(css->file, css->text_line, css->text_pos, label->type); @@ -350,30 +471,14 @@ static int __css_parse_obj(abc_obj_t* css, abc_char_t* c) return -ENOMEM; } - obj->css_dot = dot; - obj->css_colon = colon; - obj->flags = label->flags; - obj->keys = label->names; - obj->parent = css; - - switch (obj->type) { - case ABC_CSS_ID: - case ABC_CSS_CLASS: - case ABC_CSS_COMMA: + obj->flags = label->flags; + obj->keys = label->names; + obj->parent = css; - case ABC_CSS_LINK: - case ABC_CSS_VISITED: - case ABC_CSS_HOVER: - case ABC_CSS_ACTIVE: - obj->text = key; - break; - - default: - scf_string_free(key); - break; - }; + obj->text = key; key = NULL; + int ret; switch (tmp) { case '{': ret = __css_parse_attr(css, obj, label->attrs, label->n_attrs); @@ -383,12 +488,14 @@ static int __css_parse_obj(abc_obj_t* css, abc_char_t* c) } break; default: - ret = 0; break; }; - scf_list_add_tail(&css->childs, &obj->list); - return 0; + ret = __css_parse_comma(css, obj); + if (ret < 0) + abc_obj_free(obj); + + return ret; } int abc_css_parse(abc_obj_t* css) @@ -484,6 +591,7 @@ int abc_css_parse(abc_obj_t* css) if ('#' == c->c || ('a' <= c->c && 'z' >= c->c) + || ('A' <= c->c && 'Z' >= c->c) || (0x4e00 <= c->c && 0x9fa5 >= c->c) || '.' == c->c) { @@ -630,51 +738,73 @@ static void __css_set_attr(abc_obj_t* obj, abc_obj_t* attr) }; } -static void __css_set_status(abc_obj_t* obj, abc_obj_t* status) +static void __css_set_pse(abc_obj_t* obj, abc_obj_t* pse, int colon) { - scf_list_t* l; - abc_obj_t* attr; - abc_obj_t* attr2; - abc_obj_t* status2; + scf_string_t* s; + scf_list_t* l; + abc_obj_t* attr; + abc_obj_t* attr2; + abc_obj_t* pse2; - if (scf_list_empty(&status->attrs)) + if (scf_list_empty(&pse->attrs)) return; - status2 = abc_obj_alloc(status->file, status->line, status->pos, status->type); - if (!status2) + if (colon > 0 && ':' == pse->text->data[colon - 1]) + colon--; + + char* key = pse->text->data + colon; + int len = pse->text->len - colon; + + pse2 = abc_obj_get_attr2(obj, key, len); + if (!pse2) { + pse2 = abc_obj_alloc(pse->file, pse->line, pse->pos, pse->type); + if (!pse2) + return; + + pse2->flags = pse->flags; + pse2->keys = pse->keys; + } + + s = scf_string_clone(pse->text); + if (!s) return; - status2->flags = status->flags; - status2->keys = status->keys; - for (l = scf_list_head(&status->attrs); l != scf_list_sentinel(&status->attrs); l = scf_list_next(l)) { - attr = scf_list_data(l, abc_obj_t, list); + if (pse2->text) + scf_string_free(pse2->text); - if (attr->value && attr->value->len > 0) - { - if (ABC_CSS_LINK == status->type) - __css_set_attr(obj, attr); + pse2->text = s; - attr2 = abc_obj_alloc(attr->file, attr->line, attr->pos, attr->type); - if (!attr2) { - abc_obj_free(status2); - return; - } + for (l = scf_list_head(&pse->attrs); l != scf_list_sentinel(&pse->attrs); l = scf_list_next(l)) { + attr = scf_list_data(l, abc_obj_t, list); - attr2->value = scf_string_clone(attr->value); - if (!attr2->value) { - abc_obj_free(attr2); - abc_obj_free(status2); - return; - } + if (!attr->value || 0 == attr->value->len) + continue; - attr2->keys = attr->keys; - attr2->flags = attr->flags; + attr2 = abc_obj_alloc(attr->file, attr->line, attr->pos, attr->type); + if (!attr2) + return; - scf_list_add_tail(&status2->attrs, &attr2->list); + attr2->value = scf_string_clone(attr->value); + if (!attr2->value) { + abc_obj_free(attr2); + return; } - } - scf_list_add_tail(&obj->attrs, &status2->list); + attr2->keys = attr->keys; + attr2->flags = attr->flags; + + scf_list_add_tail(&pse2->attrs, &attr2->list); + + switch (pse->css_pse_type) + { + case ABC_CSS_FIRST_CHILD: + case ABC_CSS_LINK: + __css_set_attr(obj, attr); + break; + default: + break; + }; + } } static void __css_merge(abc_obj_t* dst, abc_obj_t* src) @@ -707,7 +837,6 @@ int abc_css_merge(abc_html_t* html, abc_obj_t* css) else scf_list_mov2(&html->css->childs, &css->childs); - scf_list_t h; scf_list_t* s; scf_list_t* d; abc_obj_t* src; // merge from @@ -720,28 +849,10 @@ int abc_css_merge(abc_html_t* html, abc_obj_t* css) for (d = s; d != scf_list_sentinel(&html->css->childs); d = scf_list_prev(d)) { dst = scf_list_data(d, abc_obj_t, list); - if (src->type != dst->type) - continue; - - int ret = -1; - - switch (dst->type) { - case ABC_CSS_ID: - case ABC_CSS_CLASS: - - case ABC_CSS_LINK: - case ABC_CSS_VISITED: - case ABC_CSS_HOVER: - case ABC_CSS_ACTIVE: - ret = scf_string_cmp(src->text, dst->text); - break; - - default: - ret = __html_strcmp(src->keys[0], dst->keys[0]); - break; - }; + if (src->type == dst->type) { + if (src->type > ABC_HTML_NB && scf_string_cmp(src->text, dst->text)) + continue; - if (0 == ret) { __css_merge(dst, src); scf_list_del(&src->list); @@ -751,7 +862,15 @@ int abc_css_merge(abc_html_t* html, abc_obj_t* css) } } - scf_list_init(&h); + scf_list_t h_id; + scf_list_t h_class; + scf_list_t h_pse_class; + scf_list_t h_pse_element; + + scf_list_init(&h_id); + scf_list_init(&h_class); + scf_list_init(&h_pse_class); + scf_list_init(&h_pse_element); for (s = scf_list_head(&html->css->childs); s != scf_list_sentinel(&html->css->childs); ) { src = scf_list_data(s, abc_obj_t, list); @@ -759,101 +878,363 @@ int abc_css_merge(abc_html_t* html, abc_obj_t* css) switch (src->type) { case ABC_CSS_ID: + scf_list_del(&src->list); + scf_list_add_tail(&h_id, &src->list); + break; + case ABC_CSS_CLASS: + scf_list_del(&src->list); + scf_list_add_tail(&h_class, &src->list); + break; - case ABC_CSS_LINK: - case ABC_CSS_VISITED: - case ABC_CSS_HOVER: - case ABC_CSS_ACTIVE: + case ABC_CSS_PSE_CLASS: + scf_list_del(&src->list); + scf_list_add_tail(&h_pse_class, &src->list); + break; + + case ABC_CSS_PSE_ELEMENT: scf_list_del(&src->list); - scf_list_add_tail(&h, &src->list); + scf_list_add_tail(&h_pse_element, &src->list); break; default: break; }; } - scf_list_mov2(&html->css->childs, &h); + // sort css priority, the last is the highest + scf_list_mov2(&html->css->childs, &h_pse_element); + scf_list_mov2(&html->css->childs, &h_pse_class); + scf_list_mov2(&html->css->childs, &h_class); + scf_list_mov2(&html->css->childs, &h_id); return 0; } -int abc_css_use(abc_html_t* html, abc_obj_t* obj) +static abc_obj_t* __css_filter_pse(abc_obj_t* current, abc_obj_t* pse) { - if (!html || !obj) - return -EINVAL; + scf_list_t* l; + abc_obj_t* parent = current->parent; + abc_obj_t* tmp; - if (!html->css) - return 0; + switch (pse->type) + { + case ABC_CSS_FIRST_CHILD: + if (!parent) + return NULL; + + for (l = scf_list_prev(¤t->list); l != scf_list_sentinel(&parent->childs); l = scf_list_prev(l)) { + tmp = scf_list_data(l, abc_obj_t, list); + if (tmp->type == current->type) + return NULL; + } + break; + + default: + break; + }; + + return current; +} + +static abc_obj_t* __css_select_by_key(abc_obj_t* css, abc_obj_t* current, scf_vector_t* vec, int next, const char* key, int len) +{ scf_list_t* l; - scf_list_t* l2; - abc_obj_t* css; - abc_obj_t* __obj; - abc_obj_t* attr; + abc_obj_t* tmp; + abc_obj_t* pse; - for (l = scf_list_head(&html->css->childs); l != scf_list_sentinel(&html->css->childs); l = scf_list_next(l)) { - css = scf_list_data(l, abc_obj_t, list); + assert(next >= 0 && next < vec->size); - if (ABC_CSS_ID == css->type || ABC_CSS_CLASS == css->type) - { - attr = abc_obj_get_attr(obj, css->type); - if (!attr) - continue; + int j = (intptr_t)vec->data[next]; + int k; + int c = 0; + if (j < css->text->len) + c = css->text->data[j]; - if (__html_strcmp(attr->value->data, css->text->data + css->css_dot + 1)) - continue; + switch (c) { + case ' ': + for (current = current->parent; current; current = current->parent) + { + if (!abc_obj_cmp_keys(current, key, len)) + break; + } + break; - if (css->css_dot > 0) { - if (__html_strncmp(obj->keys[0], css->text->data, css->css_dot)) - continue; + case '>': + current = current->parent; + + if (abc_obj_cmp_keys(current, key, len)) + return NULL; + break; + + case '+': + if (!current->parent) + return NULL; + + l = scf_list_prev(¤t->list); + if (l == scf_list_sentinel(¤t->parent->childs)) + return NULL; + current = scf_list_data(l, abc_obj_t, list); + + if (abc_obj_cmp_keys(current, key, len)) + return NULL; + break; + + case '~': + if (!current->parent) + return NULL; + + for (l = scf_list_prev(¤t->list); l != scf_list_sentinel(¤t->parent->childs); l = scf_list_prev(l)) { + tmp = scf_list_data(l, abc_obj_t, list); + + if (!abc_obj_cmp_keys(tmp, key, len)) + break; } - } else if (ABC_CSS_COMMA == css->type) { - int c; - int c0 = ' '; - uint8_t* p0 = NULL; - uint8_t* p = css->text->data; + if (l == scf_list_sentinel(¤t->parent->childs)) + return NULL; - do { - c = *p; + current = tmp; + break; - if (!c || ' ' == c || '\t' == c || '\r' == c || '\n' == c || ',' == c) { - if ( ' ' == c0 || '\t' == c0 || '\r' == c0 || '\n' == c0 || ',' == c0) - goto next; + case ':': + assert(next - 1 >= 0 && next - 1 < vec->size); - if (__html_strncmp(obj->keys[0], p0, (size_t)(p - p0))) { - p0 = NULL; - goto next; - } + scf_logd("current: %s, key: %.*s, j: %d, next: %s\n", current->keys[0], len, key, j, css->text->data + j); - abc_obj_set_css(obj, css); + k = (intptr_t) vec->data[next - 1]; + if (k <= j) + return NULL; + + current = __css_select_by_key(css, current, vec, next - 1, key, len); + if (!current) + return NULL; + + pse = abc_obj_get_attr2(current, css->text->data + j, k - j); + if (!pse) + return NULL; + + if (css->css_pse_type < pse->type) + css->css_pse_type = pse->type; + + current = __css_filter_pse(current, pse); + if (!current) + return NULL; + + scf_logd("current: %s, key: %.*s, j: %d, k: %d, next: %.*s\n", current->keys[0], len, key, j, k, + k - j, css->text->data + j); + break; + default: + if (abc_obj_cmp_keys(current, key, len)) + return NULL; + break; + }; + + return current; +} + +static abc_obj_t* __css_select_by_attr(abc_obj_t* css, abc_obj_t* current, scf_vector_t* vec, int next, int type, const char* key, int len) +{ + scf_list_t* l; + abc_obj_t* tmp; + abc_obj_t* attr; + + assert(next >= 0 && next < vec->size); + + int j = (intptr_t)vec->data[next]; + int k; + int c = 0; + if (j < css->text->len) + c = css->text->data[j]; + + switch (c) { + case ' ': + for (current = current->parent; current; current = current->parent) + { + attr = abc_obj_get_attr(current, type); + + if (attr && !__html_strncmp(attr->value->data, key, len)) break; - } else { - if (!p0) - p0 = p; - } -next: - c0 = c; - p++; - } while (c); + } + break; - continue; + case '>': + current = current->parent; + if (current) { + attr = abc_obj_get_attr(current, type); + + if (!attr || __html_strncmp(attr->value->data, key, len)) + return NULL; + } + break; - } else if (ABC_CSS_LINK == css->type - || ABC_CSS_VISITED == css->type - || ABC_CSS_HOVER == css->type - || ABC_CSS_ACTIVE == css->type) { + case '+': + if (!current->parent) + return NULL; - if (css->css_colon > 0) { - if (!__html_strncmp (obj->keys[0], css->text->data, css->css_colon)) - __css_set_status(obj, css); + l = scf_list_prev(¤t->list); + if (l == scf_list_sentinel(¤t->parent->childs)) + return NULL; + + current = scf_list_data(l, abc_obj_t, list); + attr = abc_obj_get_attr(current, type); + + if (!attr || __html_strncmp(attr->value->data, key, len)) + return NULL; + break; + + case '~': + if (!current->parent) + return NULL; + + for (l = scf_list_prev(¤t->list); l != scf_list_sentinel(¤t->parent->childs); l = scf_list_prev(l)) { + tmp = scf_list_data(l, abc_obj_t, list); + + attr = abc_obj_get_attr(tmp, type); + + if (attr && !__html_strncmp(attr->value->data, key, len)) + break; } - continue; - } else if (css->type != obj->type) - continue; + if (l == scf_list_sentinel(¤t->parent->childs)) + return NULL; + + current = tmp; + break; + default: + attr = abc_obj_get_attr(current, type); + + if (!attr || __html_strncmp(attr->value->data, key, len)) + return NULL; + break; + }; + + return current; +} + +static int __css_use_complex(abc_obj_t* css, abc_obj_t* obj) +{ + scf_vector_t* vec; + abc_obj_t* current = obj; + + int colon = -1; + int c; + int i; + int j = css->text->len; + + vec = scf_vector_alloc(); + if (!vec) + return -ENOMEM; + + int ret = scf_vector_add(vec, (void*)(intptr_t)j); + if (ret < 0) + goto end; + + css->css_pse_type = 0; + + for (i = css->text->len - 1; i >= 0; i--) { + c = css->text->data[i]; + + assert('\t' != c); + assert('\r' != c); + assert('\n' != c); + + char* key = css->text->data + i + 1; + int len = j - i - 1; + + ret = 0; + switch (c) { + case '#': + current = __css_select_by_attr(css, current, vec, vec->size - 1, ABC_CSS_ID, key, len); + j = i; + break; + + case '.': + current = __css_select_by_attr(css, current, vec, vec->size - 1, ABC_CSS_CLASS, key, len); + j = i; + break; + + case ':': + if (colon < 0) + colon = i; + + if (i > 0 && ':' == css->text->data[i - 1]) + { + if (j < css->text->len) { + scf_loge("pse element must be the last for combinators '%s', file: %s, line: %d\n", + css->text->data, css->file->data, css->text_line); + ret = 0; + goto end; + } + } else + j = i; + break; + + case ' ': + case '>': + case '+': + case '~': + current = __css_select_by_key(css, current, vec, vec->size - 1, key, len); + j = i; + break; + default: + if (0 == i) + current = __css_select_by_key(css, current, vec, vec->size - 1, css->text->data, j); + break; + }; + + if (!current) { + ret = 0; + goto end; + } + if (j == i) { + ret = scf_vector_add(vec, (void*)(intptr_t)j); + if (ret < 0) + goto end; + } + } + + if (colon < 0) abc_obj_set_css(obj, css); + else + __css_set_pse(obj, css, colon); + + ret = 0; +end: + scf_vector_free(vec); + return ret; +} + +int abc_css_use(abc_html_t* html, abc_obj_t* obj) +{ + if (!html || !obj) + return -EINVAL; + + if (!html->css) + return 0; + + scf_list_t* l; + abc_obj_t* css; + abc_obj_t* attr; + + for (l = scf_list_head(&html->css->childs); l != scf_list_sentinel(&html->css->childs); l = scf_list_next(l)) { + css = scf_list_data(l, abc_obj_t, list); + + switch (css->type) + { + case ABC_CSS_ID: + case ABC_CSS_CLASS: + case ABC_CSS_PSE_CLASS: + case ABC_CSS_PSE_ELEMENT: + case ABC_CSS_COMBINATOR: + __css_use_complex(css, obj); + break; + + default: + if (css->type == obj->type) + abc_obj_set_css(obj, css); + break; + }; } // for inline css below diff --git a/html/abc_css_color.c b/html/abc_css_color.c index e2c2945..330474f 100644 --- a/html/abc_css_color.c +++ b/html/abc_css_color.c @@ -30,37 +30,37 @@ static char* css_CornflowerBlue[] = {"cornflowerblue", NULL}; static char* css_Cornsilk[] = {"cornsilk", NULL}; static char* css_Crimson[] = {"crimson", "深红", "绯红", NULL}; static char* css_Cyan[] = {"cyan", NULL}; -static char* css_DarkBlue[] = {"darkblue", "深蓝", "黛蓝", NULL}; -static char* css_DarkCyan[] = {"darkcyan", NULL}; -static char* css_DarkGoldenRod[] = {"darkgoldenrod", NULL}; -static char* css_DarkGray[] = {"darkgray", NULL}; -static char* css_DarkGreen[] = {"darkgreen", "暗绿", NULL}; -static char* css_DarkKhaki[] = {"darkkhaki", NULL}; -static char* css_DarkMagenta[] = {"darkmagenta", NULL}; -static char* css_DarkOliveGreen[] = {"darkolivegreen", NULL}; -static char* css_DarkOrange[] = {"darkorange", NULL}; -static char* css_DarkOrchid[] = {"darkorchid", NULL}; -static char* css_DarkRed[] = {"darkred", "暗红", NULL}; -static char* css_DarkSalmon[] = {"darksalmon", NULL}; -static char* css_DarkSeaGreen[] = {"darkseagreen", NULL}; -static char* css_DarkSlateBlue[] = {"darkslateblue", NULL}; -static char* css_DarkSlateGray[] = {"darkslategray", NULL}; -static char* css_DarkTurquoise[] = {"darkturquoise", NULL}; -static char* css_DarkViolet[] = {"darkviolet", NULL}; -static char* css_DeepPink[] = {"deeppink", NULL}; -static char* css_DeepSkyBlue[] = {"deepskyblue", NULL}; -static char* css_DimGray[] = {"dimgray", NULL}; -static char* css_DodgerBlue[] = {"dodgerblue", NULL}; -static char* css_FireBrick[] = {"firebrick", NULL}; -static char* css_FloralWhite[] = {"floralwhite", NULL}; -static char* css_ForestGreen[] = {"forestgreen", NULL}; +static char* css_DarkBlue[] = {"DarkBlue", "深蓝", "黛蓝", NULL}; +static char* css_DarkCyan[] = {"DarkCyan", NULL}; +static char* css_DarkGoldenRod[] = {"DarkGoldenRod", NULL}; +static char* css_DarkGray[] = {"DarkGray", "DarkGrey", NULL}; +static char* css_DarkGreen[] = {"DarkGreen", "暗绿", NULL}; +static char* css_DarkKhaki[] = {"DarkKhaki", NULL}; +static char* css_DarkMagenta[] = {"DarkMagenta", NULL}; +static char* css_DarkOliveGreen[] = {"DarkOliveGreen", NULL}; +static char* css_DarkOrange[] = {"DarkOrange", NULL}; +static char* css_DarkOrchid[] = {"DarkOrchid", NULL}; +static char* css_DarkRed[] = {"DarkRed", "暗红", NULL}; +static char* css_DarkSalmon[] = {"DarkSalmon", NULL}; +static char* css_DarkSeaGreen[] = {"DarkSeaGreen", NULL}; +static char* css_DarkSlateBlue[] = {"DarkSlateBlue", NULL}; +static char* css_DarkSlateGray[] = {"DarkSlateGray", "DarkSlateGrey", NULL}; +static char* css_DarkTurquoise[] = {"DarkTurquoise", NULL}; +static char* css_DarkViolet[] = {"DarkViolet", NULL}; +static char* css_DeepPink[] = {"DeepPink", NULL}; +static char* css_DeepSkyBlue[] = {"DeepskyBlue", NULL}; +static char* css_DimGray[] = {"DimGray", "DimGrey", NULL}; +static char* css_DodgerBlue[] = {"DodgerBlue", NULL}; +static char* css_FireBrick[] = {"FireBrick", NULL}; +static char* css_FloralWhite[] = {"FloralWhite", NULL}; +static char* css_ForestGreen[] = {"ForestGreen", NULL}; static char* css_fuchsia[] = {"fuchsia", "紫红", "洋红", NULL}; static char* css_Gainsboro[] = {"Gainsboro", NULL}; static char* css_GhostWhite[] = {"GhostWhite", NULL}; static char* css_Gold[] = {"Gold", "金", "金黄", NULL}; static char* css_GoldenRod[] = {"GoldenRod", NULL}; -static char* css_Gray[] = {"Gray", "灰", NULL}; +static char* css_Gray[] = {"Gray", "Grey", "灰", NULL}; static char* css_green[] = {"green", "绿", NULL}; static char* css_GreenYellow[] = {"GreenYellow", "黄绿", NULL}; @@ -79,13 +79,13 @@ static char* css_LightCoral[] = {"LightCoral", NULL}; static char* css_LightCyan[] = {"LightCyan", NULL}; static char* css_LightGoldenRodYellow[] = {"LightGoldenRodYellow", NULL}; -static char* css_LightGray[] = {"LightGray", "浅灰", NULL}; -static char* css_LightGreen[] = {"LightGreen", "浅绿", NULL}; +static char* css_LightGray[] = {"LightGray", "LightGrey", "浅灰", NULL}; +static char* css_LightGreen[] = {"LightGreen", "浅绿", NULL}; static char* css_LightPink[] = {"LightPink", NULL}; static char* css_LightSalmon[] = {"LightSalmon", NULL}; -static char* css_LightSeaGreen[] = {"LightSeaGreen", "浅海绿", NULL}; -static char* css_LightSkyBlue[] = {"LightSkyBlue", "浅天蓝", NULL}; -static char* css_LightSlateGray[] = {"LightSlateGray", NULL}; +static char* css_LightSeaGreen[] = {"LightSeaGreen", "浅海绿", NULL}; +static char* css_LightSkyBlue[] = {"LightSkyBlue", "浅天蓝", NULL}; +static char* css_LightSlateGray[] = {"LightSlateGray", "LightSlateGrey", NULL}; static char* css_LightSteelBlue[] = {"LightSteelBlue", NULL}; static char* css_LightYellow[] = {"LightYellow", "浅黄", NULL}; static char* css_Lime[] = {"Lime", NULL}; @@ -139,12 +139,12 @@ static char* css_SandyBrown[] = {"SandyBrown", NULL}; static char* css_SeaGreen[] = {"SeaGreen", "海绿", NULL}; static char* css_SeaShell[] = {"SeaShell", NULL}; static char* css_Sienna[] = {"Sienna", NULL}; -static char* css_Silver[] = {"Silver", "银", NULL}; -static char* css_SkyBlue[] = {"SkyBlue", "天蓝", NULL}; +static char* css_Silver[] = {"Silver", "银", NULL}; +static char* css_SkyBlue[] = {"SkyBlue", "天蓝", NULL}; static char* css_SlateBlue[] = {"SlateBlue", NULL}; -static char* css_SlateGray[] = {"SlateGray", NULL}; -static char* css_Snow[] = {"Snow", "雪白", NULL}; -static char* css_SpringGreen[] = {"SpringGreen", "春绿", NULL}; +static char* css_SlateGray[] = {"SlateGray", "SlateGrey", NULL}; +static char* css_Snow[] = {"Snow", "雪白", NULL}; +static char* css_SpringGreen[] = {"SpringGreen", "春绿", NULL}; static char* css_SteelBlue[] = {"SteelBlue", NULL}; static char* css_Tan[] = {"Tan", NULL}; diff --git a/html/abc_html.c b/html/abc_html.c index bd4f58b..f2f2715 100644 --- a/html/abc_html.c +++ b/html/abc_html.c @@ -107,14 +107,18 @@ static char* progress_keys[] = {"progress", "进度条", NULL}; static char* script_keys[] = {"script", "脚本", NULL}; static char* style_keys[] = {"style", "样式", NULL}; -static char* css_id_keys[] = {"#", NULL}; -static char* css_class_keys[] = {".", NULL}; -static char* css_comma_keys[] = {",", NULL}; +static char* css_id_keys[] = {"#", NULL}; +static char* css_class_keys[] = {".", NULL}; +static char* css_other_keys[] = {"*", NULL}; +static char* css_pse_class_keys[] = {":", NULL}; +static char* css_pse_element_keys[] = {"::", NULL}; -static char* css_link_keys[] = {":link", ":未访问", NULL}; -static char* css_visited_keys[] = {":visited", ":已访问", NULL}; -static char* css_hover_keys[] = {":hover", ":已关注", NULL}; -static char* css_active_keys[] = {":active", ":已激活", NULL}; +static char* css_link_keys[] = {":link", "未访问", NULL}; +static char* css_visited_keys[] = {":visited", "已访问", NULL}; +static char* css_hover_keys[] = {":hover", "正在指向", NULL}; +static char* css_active_keys[] = {":active", "正在点击", NULL}; + +static char* css_first_child_keys[] = {":first-child", "第1个子元素", "第一个子元素", NULL}; static html_attr_t html_attrs[] = { @@ -127,7 +131,7 @@ static html_attr_t meta_attrs[] = { {http_equiv_keys, "", ABC_HTML_ATTR_HTTP_EQUIV, ABC_HTML_FLAG_SHOW}, {content_keys, "", ABC_HTML_ATTR_CONTENT, ABC_HTML_FLAG_SHOW}, -// {charset_keys, "", ABC_HTML_ATTR_CHARSET, ABC_HTML_FLAG_SHOW}, + {charset_keys, "", ABC_HTML_ATTR_CHARSET, ABC_HTML_FLAG_SHOW}, }; static html_attr_t body_attrs[] = @@ -240,6 +244,8 @@ static html_attr_t p_attrs[] = ABC_CSS_FONT(SimSong, 16, black, ) ABC_CSS_TEXT(left, ) + + {css_first_child_keys, "", ABC_CSS_FIRST_CHILD, ABC_HTML_FLAG_SHOW}, }; static html_attr_t css_id_attrs[] = @@ -269,6 +275,8 @@ static html_attr_t b_attrs[] = ABC_CSS_FONT(SimHei, 16, , bold) ABC_CSS_TEXT(left, ) + + {css_first_child_keys, "", ABC_CSS_FIRST_CHILD, ABC_HTML_FLAG_SHOW}, }; static html_attr_t i_attrs[] = @@ -279,6 +287,8 @@ static html_attr_t i_attrs[] = ABC_CSS_FONT(SimSong, 16, , italic) ABC_CSS_TEXT(left, ) + + {css_first_child_keys, "", ABC_CSS_FIRST_CHILD, ABC_HTML_FLAG_SHOW}, }; static html_attr_t a_attrs[] = @@ -290,7 +300,11 @@ static html_attr_t a_attrs[] = ABC_CSS_FONT(SimSong, 16, blue, ) ABC_CSS_TEXT(left, underline) - {href_keys, "", ABC_HTML_ATTR_HREF, ABC_HTML_FLAG_SHOW}, + {href_keys, "", ABC_HTML_ATTR_HREF, ABC_HTML_FLAG_SHOW}, + {css_link_keys, "", ABC_CSS_LINK, ABC_HTML_FLAG_SHOW}, + {css_visited_keys, "", ABC_CSS_VISITED, ABC_HTML_FLAG_SHOW}, + {css_hover_keys, "", ABC_CSS_HOVER, ABC_HTML_FLAG_SHOW}, + {css_active_keys, "", ABC_CSS_ACTIVE, ABC_HTML_FLAG_SHOW}, }; static html_attr_t link_attrs[] = @@ -423,7 +437,7 @@ static html_attr_t script_attrs[] = static html_label_t html_labels[] = { {html_keys, ABC_HTML, abc_number_of(html_attrs), html_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, - {meta_keys, ABC_HTML_META, abc_number_of(meta_attrs), meta_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SINGLE}, + {meta_keys, ABC_HTML_META, abc_number_of(meta_attrs), meta_attrs, ABC_HTML_FLAG_OPEN | ABC_HTML_FLAG_SHOW}, {head_keys, ABC_HTML_HEAD, 0, NULL, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, {title_keys, ABC_HTML_TITLE, 0, NULL, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, {body_keys, ABC_HTML_BODY, abc_number_of(body_attrs), body_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, @@ -444,7 +458,7 @@ static html_label_t html_labels[] = {hr_keys, ABC_HTML_HR, 0, NULL, ABC_HTML_FLAG_OPEN | ABC_HTML_FLAG_SHOW}, {a_keys, ABC_HTML_A, abc_number_of(a_attrs), a_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, - {img_keys, ABC_HTML_IMG, abc_number_of(img_attrs), img_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SINGLE | ABC_HTML_FLAG_SHOW}, + {img_keys, ABC_HTML_IMG, abc_number_of(img_attrs), img_attrs, ABC_HTML_FLAG_OPEN | ABC_HTML_FLAG_SHOW}, {link_keys, ABC_HTML_LINK, abc_number_of(link_attrs), link_attrs, ABC_HTML_FLAG_OPEN | ABC_HTML_FLAG_SHOW}, {form_keys, ABC_HTML_FORM, abc_number_of(form_attrs), form_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, @@ -470,14 +484,11 @@ static html_label_t html_labels[] = {script_keys, ABC_HTML_SCRIPT, abc_number_of(script_attrs), script_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, {style_keys, ABC_HTML_STYLE, 0, NULL, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, - {css_id_keys, ABC_CSS_ID, abc_number_of(css_id_attrs), css_id_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, - {css_class_keys, ABC_CSS_CLASS, abc_number_of(css_id_attrs), css_id_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, - {css_comma_keys, ABC_CSS_COMMA, abc_number_of(css_id_attrs), css_id_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, - - {css_link_keys, ABC_CSS_LINK, abc_number_of(css_id_attrs), css_id_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, - {css_visited_keys, ABC_CSS_VISITED, abc_number_of(css_id_attrs), css_id_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, - {css_hover_keys, ABC_CSS_HOVER, abc_number_of(css_id_attrs), css_id_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, - {css_active_keys, ABC_CSS_ACTIVE, abc_number_of(css_id_attrs), css_id_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, + {css_id_keys, ABC_CSS_ID, abc_number_of(css_id_attrs), css_id_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, + {css_class_keys, ABC_CSS_CLASS, abc_number_of(css_id_attrs), css_id_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, + {css_pse_class_keys, ABC_CSS_PSE_CLASS, abc_number_of(css_id_attrs), css_id_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, + {css_pse_element_keys, ABC_CSS_PSE_ELEMENT, abc_number_of(css_id_attrs), css_id_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, + {css_other_keys, ABC_CSS_COMBINATOR, abc_number_of(css_id_attrs), css_id_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW}, }; static int __html_parse_obj(abc_html_t* html, abc_char_t* c); @@ -881,31 +892,21 @@ static int __html_parse_text(abc_html_t* html, abc_obj_t* obj) abc_obj_t* mov = NULL; if ('/' != c->c) { - if (ABC_HTML_BODY == obj->type - || ABC_HTML_H1 == obj->type - || ABC_HTML_H2 == obj->type - || ABC_HTML_H3 == obj->type - || ABC_HTML_H4 == obj->type - || ABC_HTML_H5 == obj->type - || ABC_HTML_H6 == obj->type - || ABC_HTML_P == obj->type) { - - if (obj->text) { + if (obj->text) { #define HTML_MOV_TEXT() \ - do { \ - mov = abc_obj_alloc(NULL, obj->line, obj->pos, obj->type); \ - if (!mov) \ - return -ENOMEM; \ - mov->text = obj->text; \ - obj->text = NULL; \ - mov->parent = obj; \ - mov->flags = obj->flags; \ - scf_list_add_tail(&obj->childs, &mov->list); \ - scf_logd("--- %s, %s\n", obj->keys[0], mov->text->data); \ - } while (0) - - HTML_MOV_TEXT(); - } + do { \ + mov = abc_obj_alloc(NULL, obj->line, obj->pos, ABC_CORE_TEXT); \ + if (!mov) \ + return -ENOMEM; \ + mov->text = obj->text; \ + obj->text = NULL; \ + mov->parent = obj; \ + mov->flags = obj->flags; \ + scf_list_add_tail(&obj->childs, &mov->list); \ + scf_logd("--- %s, %s\n", obj->keys[0], mov->text->data); \ + } while (0) + + HTML_MOV_TEXT(); } int ret = __html_parse_obj(html, c); @@ -922,18 +923,8 @@ static int __html_parse_text(abc_html_t* html, abc_obj_t* obj) return ret; } - if (obj->text && !scf_list_empty(&obj->childs)) { - - if (ABC_HTML_BODY == obj->type - || ABC_HTML_H1 == obj->type - || ABC_HTML_H2 == obj->type - || ABC_HTML_H3 == obj->type - || ABC_HTML_H4 == obj->type - || ABC_HTML_H5 == obj->type - || ABC_HTML_H6 == obj->type - || ABC_HTML_P == obj->type) - HTML_MOV_TEXT(); - } + if (obj->text && !scf_list_empty(&obj->childs)) + HTML_MOV_TEXT(); html->pos++; free(c); @@ -1031,7 +1022,8 @@ static int __html_parse_attr2(abc_html_t* html, abc_obj_t* obj, const html_attr_ if ('_' == c->c || '-' == c->c || ':' == c->c - || ('a' <= c->c && 'z' >= c->c)) { + || ('a' <= c->c && 'z' >= c->c) + || ('Z' <= c->c && 'Z' >= c->c)) { scf_string_cat_cstr_len(key, c->utf8, c->len); } else if (' ' == c->c) { @@ -1048,8 +1040,6 @@ static int __html_parse_attr2(abc_html_t* html, abc_obj_t* obj, const html_attr_ c = NULL; } - int tmp = c->c; - free(c); c = NULL; @@ -1470,6 +1460,7 @@ int abc_html_parse(abc_html_t* html) } if (('a' <= c->c && 'z' >= c->c) + || ('A' <= c->c && 'Z' >= c->c) || (0x4e00 <= c->c && 0x9fa5 >= c->c)) { int ret = __html_parse_obj(html, c); diff --git a/html/abc_obj.c b/html/abc_obj.c index ac1beaf..91c39d2 100644 --- a/html/abc_obj.c +++ b/html/abc_obj.c @@ -106,6 +106,45 @@ abc_obj_t* abc_obj_find_type(abc_obj_t* root, int type) return NULL; } +int abc_obj_cmp_keys(abc_obj_t* obj, const char* name, size_t len) +{ + int i; + for (i = 0; obj->keys[i]; i++) { + + if (!__html_strncmp(obj->keys[i], name, len)) + return 0; + } + return -1; +} + +int abc_obj_copy_attrs(abc_obj_t* dst, abc_obj_t* src) +{ + scf_list_t* l; + abc_obj_t* attr; + abc_obj_t* copy; + + for (l = scf_list_head(&src->attrs); l != scf_list_sentinel(&src->attrs); l = scf_list_next(l)) { + attr = scf_list_data(l, abc_obj_t, list); + + copy = abc_obj_alloc(attr->file, attr->line, attr->pos, attr->type); + if (!copy) + return -ENOMEM; + + copy->value = scf_string_clone(attr->value); + if (!copy->value) { + abc_obj_free(copy); + return -ENOMEM; + } + + copy->keys = attr->keys; + copy->flags = attr->flags; + + scf_list_add_tail(&dst->attrs, ©->list); + } + + return 0; +} + int abc_obj_set_attr(abc_obj_t* obj, int key, const char* value, size_t len) { scf_string_t* s; @@ -129,10 +168,25 @@ int abc_obj_set_attr(abc_obj_t* obj, int key, const char* value, size_t len) return -EINVAL; } +abc_obj_t* abc_obj_get_attr2(abc_obj_t* obj, const char* key, int len) +{ + scf_list_t* l; + abc_obj_t* attr; + + for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) { + attr = scf_list_data(l, abc_obj_t, list); + + if (!abc_obj_cmp_keys(attr, key, len)) + return attr; + } + + return NULL; +} + abc_obj_t* abc_obj_get_attr(abc_obj_t* obj, int key) { - scf_list_t* l; - abc_obj_t* attr; + scf_list_t* l; + abc_obj_t* attr; for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) { attr = scf_list_data(l, abc_obj_t, list); diff --git a/html/abc_obj.h b/html/abc_obj.h index 787dacd..85a8b51 100644 --- a/html/abc_obj.h +++ b/html/abc_obj.h @@ -13,6 +13,7 @@ enum abc_objs { ABC_HTML, ABC_HTML_TITLE, + ABC_HTML_META, ABC_HTML_HEAD, ABC_HTML_BODY, @@ -43,8 +44,6 @@ enum abc_objs ABC_HTML_INPUT, ABC_HTML_CENTER, - ABC_HTML_META, - ABC_HTML_TABLE, ABC_HTML_TR, ABC_HTML_TH, @@ -68,7 +67,8 @@ enum abc_objs ABC_HTML_UL, ABC_HTML_LI, - ABC_HTML_NB, // total HTML objects + ABC_CORE_TEXT, // not HTML label, only for browser core to layout & draw text + ABC_HTML_NB, // total HTML objects ABC_HTML_ATTR_ID, ABC_HTML_ATTR_TYPE, @@ -125,11 +125,6 @@ enum abc_objs ABC_HTML_ATTR_CONTROLS, // css objects - ABC_CSS_LINK, - ABC_CSS_VISITED, - ABC_CSS_HOVER, - ABC_CSS_ACTIVE, - ABC_CSS_LIST_STYLE, ABC_CSS_LIST_STYLE_TYPE, ABC_CSS_LIST_STYLE_IMAGE, @@ -137,11 +132,23 @@ enum abc_objs ABC_CSS_BORDER_COLLAPSE, - ABC_CSS_COMMA, + // css pse class or pse element, first status onload + ABC_CSS_FIRST_CHILD, + ABC_CSS_LINK, + + // status after user operations + ABC_CSS_VISITED, + ABC_CSS_HOVER, + ABC_CSS_ACTIVE, // ... new css add here - // css enum from html attrs above + // css selectors + ABC_CSS_PSE_CLASS, + ABC_CSS_PSE_ELEMENT, + ABC_CSS_COMBINATOR, + + // css selectors from html attrs above ABC_CSS_ID = ABC_HTML_ATTR_ID, ABC_CSS_CLASS = ABC_HTML_ATTR_CLASS, }; @@ -167,6 +174,9 @@ struct abc_text_s int start; int len; + + int w; + int h; }; struct abc_obj_s @@ -192,6 +202,10 @@ struct abc_obj_s #define ABC_HTML_FLAG_SHOW 4 uint32_t flags; + int d; // margin + border + padding + int w0; + int h0; + int x; int y; int w; @@ -216,8 +230,7 @@ struct abc_obj_s int text_pos; abc_io_t io; - int css_dot; // dot position for css class - int css_colon; // colon position for css status + int css_pse_type; scf_string_t* file; // file name int line; // line @@ -230,6 +243,9 @@ struct abc_obj_s uint32_t clicked:1; uint32_t visited:1; + + uint32_t w_set:1; + uint32_t h_set:1; }; abc_obj_t* abc_obj_alloc(scf_string_t* file, int line, int pos, int type); @@ -237,8 +253,13 @@ void abc_obj_free (abc_obj_t* obj); abc_obj_t* abc_obj_find (abc_obj_t* root, int x, int y); void abc_obj_print(abc_obj_t* obj); +int abc_obj_cmp_keys (abc_obj_t* obj, const char* name, size_t len); +int abc_obj_copy_attrs(abc_obj_t* dst, abc_obj_t* src); + void abc_obj_set_css (abc_obj_t* obj, abc_obj_t* css); int abc_obj_set_attr (abc_obj_t* obj, int key, const char* value, size_t len); + +abc_obj_t* abc_obj_get_attr2(abc_obj_t* obj, const char* key, int len); abc_obj_t* abc_obj_get_attr (abc_obj_t* obj, int key); abc_obj_t* abc_obj_find_attr(abc_obj_t* obj, int key); diff --git a/ui/Makefile b/ui/Makefile index e95b124..0bb821b 100644 --- a/ui/Makefile +++ b/ui/Makefile @@ -6,6 +6,8 @@ CFILES += abc_layout_title.c CFILES += abc_layout_head.c CFILES += abc_layout_body.c CFILES += abc_layout_div.c + +CFILES += abc_layout_text.c CFILES += abc_layout_h1.c CFILES += abc_layout_img.c @@ -23,11 +25,11 @@ CFILES += abc_render_html.c CFILES += abc_render_title.c CFILES += abc_render_head.c CFILES += abc_render_body.c -CFILES += abc_render_div.c CFILES += __render_border.c CFILES += __render_bg_image.c CFILES += __render_text.c +CFILES += abc_render_text.c CFILES += abc_render_empty.c diff --git a/ui/__render_text.c b/ui/__render_text.c index 6ca9a8a..609e4bc 100644 --- a/ui/__render_text.c +++ b/ui/__render_text.c @@ -38,11 +38,14 @@ static int __render_fini_text(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) +void __draw_text(cairo_text_extents_t* extents, cairo_t* cr, const char* text, double x, double y, double y_bearing, int line_type, int line_width) { cairo_text_extents(cr, text, extents); double w = extents->width; + if (extents->y_bearing > y_bearing) + extents->y_bearing = y_bearing; + x += extents->x_bearing; y -= extents->y_bearing; @@ -136,7 +139,7 @@ int __init_text(cairo_t* cr, abc_obj_t* obj, int num_flag) } if (!obj->text_splits) { - __draw_text(&extents, cr, obj->text->data, obj->parent->padding, 0.0, line_type, line_width); + __draw_text(&extents, cr, obj->text->data, obj->parent->padding, 0.0, obj->y_bearing, line_type, line_width); return 0; } @@ -155,7 +158,7 @@ int __init_text(cairo_t* cr, abc_obj_t* obj, int num_flag) return -ENOMEM; } - __draw_text(&extents, cr, s->data, x, y, line_type, line_width); + __draw_text(&extents, cr, s->data, x, y, obj->y_bearing, line_type, line_width); y += extents.height; obj->text_splits = t->next; diff --git a/ui/abc.h b/ui/abc.h index ba523a1..50174ee 100644 --- a/ui/abc.h +++ b/ui/abc.h @@ -76,7 +76,10 @@ int __init_program(GLuint* pprog, const char* vert_shader, const char* frag_sha int __init_buffers(GLuint* vao, GLuint buffers[2]); -int __init_text(cairo_t* cr, abc_obj_t* obj, int num_flag); +void __draw_text(cairo_text_extents_t* extents, cairo_t* cr, const char* text, double x, double y, double y_bearing, int line_type, int line_width); + +int __init_text (cairo_t* cr, abc_obj_t* obj, int num_flag); +int __layout_text(cairo_t* cr, abc_obj_t* obj, int x, int width, cairo_text_extents_t* extents); extern abc_render_t __render_border; extern abc_render_t __render_bg_image; diff --git a/ui/abc_layout.c b/ui/abc_layout.c index 1dd4fd5..a7e7796 100644 --- a/ui/abc_layout.c +++ b/ui/abc_layout.c @@ -5,6 +5,8 @@ int abc_layout_title(abc_layout_t* layout, abc_obj_t* obj, int width, int height int abc_layout_head (abc_layout_t* layout, abc_obj_t* obj, int width, int height); int abc_layout_body (abc_layout_t* layout, abc_obj_t* obj, int width, int height); +int abc_layout_text(abc_layout_t* layout, abc_obj_t* obj, int width, int height); + int abc_layout_div (abc_layout_t* layout, abc_obj_t* obj, int width, int height); int abc_layout_h1 (abc_layout_t* layout, abc_obj_t* obj, int width, int height); int abc_layout_img (abc_layout_t* layout, abc_obj_t* obj, int width, int height); @@ -28,11 +30,12 @@ static abc_layout_pt abc_layouts[ABC_HTML_NB] = { [ABC_HTML] = abc_layout_html, [ABC_HTML_TITLE] = abc_layout_title, + [ABC_HTML_META] = abc_layout_empty, [ABC_HTML_HEAD] = abc_layout_head, [ABC_HTML_BODY] = abc_layout_body, [ABC_HTML_DIV] = abc_layout_div, - [ABC_HTML_META] = abc_layout_empty, + [ABC_CORE_TEXT] = abc_layout_text, [ABC_HTML_H1] = abc_layout_h1, [ABC_HTML_H2] = abc_layout_h1, @@ -71,8 +74,8 @@ static abc_layout_pt abc_layouts[ABC_HTML_NB] = [ABC_HTML_STYLE] = abc_layout_empty, [ABC_HTML_LINK] = abc_layout_empty, - [ABC_HTML_B] = abc_layout_h1, - [ABC_HTML_I] = abc_layout_h1, + [ABC_HTML_B] = abc_layout_text, + [ABC_HTML_I] = abc_layout_text, }; int abc_layout_obj(abc_layout_t* layout, abc_obj_t* obj, int width, int height) @@ -107,20 +110,21 @@ int abc_layout_css(abc_obj_t* root) abc_obj_t* parent = root->parent; abc_obj_t* attr; - attr = abc_obj_get_attr(root, ABC_HTML_ATTR_TEXT_ALIGN); - if (attr) { - if (!parent || parent->w <= 0 || parent->h <= 0) - return 0; + if (!parent || parent->w <= 0) + return 0; - switch (parent->type) - { - case ABC_HTML_BODY: - break; - default: - return 0; - break; - } + switch (parent->type) + { + case ABC_HTML_BODY: + case ABC_HTML_DIV: + break; + default: + return 0; + break; + }; + attr = abc_obj_get_attr(root, ABC_HTML_ATTR_TEXT_ALIGN); + if (attr) { int x = root->x; if (!__html_strcmp(attr->value->data, "center")) @@ -137,16 +141,15 @@ int abc_layout_css(abc_obj_t* root) int abc_layout_root(abc_ctx_t* abc, abc_obj_t* root, int width, int height) { - scf_string_t* key; - scf_list_t* l; - abc_obj_t* child; - if (!root) return -EINVAL; if (width <= 0 || height <= 0) return -EINVAL; + int root_w = width; + int root_h = height; + switch (root->type) { case ABC_HTML_SCRIPT: @@ -156,15 +159,36 @@ int abc_layout_root(abc_ctx_t* abc, abc_obj_t* root, int width, int height) break; case ABC_HTML_BODY: - root->w = width; - root->h = height; + case ABC_HTML_DIV: + root->d = abc_css_margin(root); + root->w_set = abc_css_width (root, width, root->d); + root->h_set = abc_css_height(root, height, root->d); + + if (root->w_set) { + root->w0 = root->w; + root_w = root->w; + } else { +// if (ABC_HTML_BODY != root->type) +// root->w = 0; + } + + if (root->h_set) { + root->h0 = root->h; + root_h = root->h; + } else { + if (ABC_HTML_BODY != root->type) + root->h = 0; + } break; default: break; }; - int x = root->x + 2; - int y = root->y + 2; + scf_list_t* l; + abc_obj_t* child; + + int x = root->x + root->d; + int y = root->y + root->d; int h = 0; int X = 0; @@ -182,40 +206,59 @@ int abc_layout_root(abc_ctx_t* abc, abc_obj_t* root, int width, int height) case ABC_HTML_H4: case ABC_HTML_H5: case ABC_HTML_H6: + case ABC_HTML_DIV: case ABC_HTML_OL: case ABC_HTML_UL: case ABC_HTML_LI: - child->x = root->x + 2; + child->x = root->x + root->d; child->y = y + h; - ret = abc_layout_root(NULL, child, width, height); + ret = abc_layout_root(NULL, child, root_w, root_h); if (ret < 0) return ret; - x = root->x + 2; + x = root->x + root->d; y = child->y + child->h; h = 0; break; case ABC_HTML_BR: case ABC_HTML_TR: - x = root->x + 2; + x = root->x + root->d; y += h; h = 0; default: - if (child->type == root->type && l == scf_list_head(&root->childs)) { - x = root->x; - y = root->y; - } - child->x = x; child->y = y; - ret = abc_layout_root(NULL, child, width, height); + ret = abc_layout_root(NULL, child, root_w, root_h); if (ret < 0) return ret; - if (x + child->w < width) { + if (ABC_CORE_TEXT == child->type && child->text_splits) { + abc_text_t* t = child->text_splits; + + if (h < t->h) + h = t->h; + + t = t->next; + while (t && t->next) { + h += t->h; + t = t->next; + } + + x = root->x + root->d; + y += h; + h = 0; + + if (t) { + h = t->h; + x += t->w; + } + + scf_logd("--- x: %d, y: %d, h: %d ---\n\n", x, y, h); + + } else if (x + child->w < root_w) { if (h < child->h) h = child->h; @@ -223,10 +266,10 @@ int abc_layout_root(abc_ctx_t* abc, abc_obj_t* root, int width, int height) } else { y += h; - child->x = root->x + 2; + child->x = root->x + root->d; child->y = y; - ret = abc_layout_root(NULL, child, width, height); + ret = abc_layout_root(NULL, child, root_w, root_h); if (ret < 0) return ret; @@ -234,8 +277,8 @@ int abc_layout_root(abc_ctx_t* abc, abc_obj_t* root, int width, int height) x = child->x + child->w; } - if (ABC_HTML_P == child->type && ABC_HTML_P != root->type) { - x = root->x + 2; + if (ABC_HTML_P == child->type) { + x = root->x + root->d; y += h; h = 0; } @@ -249,9 +292,8 @@ int abc_layout_root(abc_ctx_t* abc, abc_obj_t* root, int width, int height) Y = child->y + child->h; } - int ret = abc_layout_obj(NULL, root, width, height); - if (ret < 0) - return ret; + X += root->d; + Y += root->d; if (root->w < X - root->x) root->w = X - root->x; @@ -259,6 +301,10 @@ int abc_layout_root(abc_ctx_t* abc, abc_obj_t* root, int width, int height) if (root->h < Y - root->y) root->h = Y - root->y; + int ret = abc_layout_obj(NULL, root, width, height); + if (ret < 0) + return ret; + abc_layout_css(root); #if 0 if (root->keys) diff --git a/ui/abc_layout_div.c b/ui/abc_layout_div.c index 6878cc0..3fd9d7c 100644 --- a/ui/abc_layout_div.c +++ b/ui/abc_layout_div.c @@ -2,5 +2,62 @@ int abc_layout_div(abc_layout_t* layout, abc_obj_t* obj, int width, int height) { + abc_obj_t* attr; + cairo_text_extents_t extents; + cairo_surface_t* surface; + cairo_t* cr; + + if (!obj->text) { + if (obj->w_set) + obj->w = obj->w0; + + if (obj->h_set) + obj->h = obj->h0; + + return 0; + } + + int size = 16; + int w; + int h; + + if (obj->w_set) + w = obj->w0 - obj->d * 2; + else + w = width - obj->d * 2; + + attr = abc_obj_find_attr(obj, ABC_HTML_ATTR_FONT_SIZE); + if (attr) + size = atoi(attr->value->data); + + if (w < size) { + scf_loge("\n"); + return -EINVAL; + } + + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, height); + cr = cairo_create(surface); + + w = w / size * size; + + int ret = __layout_text(cr, obj, 0, w, &extents); + if (ret < 0) + scf_loge("ret: %d\n", ret); + + w = extents.width + extents.x_bearing + obj->d * 2; + h = extents.height + extents.height / 2 + obj->d * 2; + + obj->w = obj->w_set ? obj->w0 : w; + obj->h = obj->h_set ? obj->h0 : h + obj->h; +// obj->w = (obj->w + 3) & ~0x3; +// obj->h = (obj->h + 3) & ~0x3; + + scf_logd("%s, w: %d, h: %d, d: %d, x_bearing: %lg, y_bearing: %lg, width: %lg, height: %lg, x_advance: %lg, y_advance: %lg, width: %d, height: %d\n", + obj->text->data, obj->w, obj->h, d, + extents.x_bearing, extents.y_bearing, extents.width, extents.height, + extents.x_advance, extents.y_advance, width, height); + + cairo_destroy(cr); + cairo_surface_destroy(surface); return 0; } diff --git a/ui/abc_layout_h1.c b/ui/abc_layout_h1.c index a620318..981b11e 100644 --- a/ui/abc_layout_h1.c +++ b/ui/abc_layout_h1.c @@ -1,127 +1,24 @@ #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; abc_obj_t* style; cairo_text_extents_t extents; - cairo_text_extents_t extents2; cairo_surface_t* surface; cairo_t* cr; - if (!obj->text) + if (!obj->text) { + scf_list_t* l; + abc_obj_t* child; + + for (l = scf_list_head(&obj->childs); l != scf_list_sentinel(&obj->childs); l = scf_list_next(l)) { + child = scf_list_data(l, abc_obj_t, list); + + child->y_bearing = obj->y_bearing; + } return 0; + } surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cr = cairo_create(surface); @@ -161,9 +58,9 @@ int abc_layout_h1(abc_layout_t* layout, abc_obj_t* obj, int width, int height) if (0 == obj->parent->padding) { scf_string_t* s = abc_ol_list_style(obj->parent, obj->parent->n_childs); if (s) { - cairo_text_extents(cr, s->data, &extents2); + cairo_text_extents(cr, s->data, &extents); - obj->parent->padding = extents2.width + extents2.x_bearing + size / 2; + obj->parent->padding = extents.width + extents.x_bearing + size / 2; scf_string_free(s); s = NULL; @@ -183,14 +80,13 @@ int abc_layout_h1(abc_layout_t* layout, abc_obj_t* obj, int width, int height) int w = (width - d * 2 - dw) / size * size; - int ret = __layout_text(&extents, cr, obj, w); + int ret = __layout_text(cr, obj, 0, w, &extents); if (ret < 0) scf_loge("ret: %d\n", ret); - if (ABC_HTML_OL == obj->parent->type) - obj->y_bearing = extents.y_bearing; + obj->y_bearing = extents.y_bearing; - w = extents.width + extents.x_bearing + d * 2 + dw; + w = extents.x_advance + extents.x_bearing + d * 2 + dw; obj->w = w; obj->h = extents.height + extents.height / 2 + d * 2; diff --git a/ui/abc_layout_text.c b/ui/abc_layout_text.c new file mode 100644 index 0000000..bc955bb --- /dev/null +++ b/ui/abc_layout_text.c @@ -0,0 +1,181 @@ +#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_t* cr, abc_obj_t* obj, int x, int width, cairo_text_extents_t* extents) +{ + 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->x_advance; + if (x + 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; + + scf_logd("x: %d, w: %d, width: %d\n", x, w, width); + + 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; + + w = (tmp.x_bearing + tmp.x_advance + size - 1) / size * size; + if (x + w <= width) { + x = 0; + + abc_text_t* t = calloc(1, sizeof(abc_text_t)); + if (!t) + return -ENOMEM; + t->start = i; + t->len = n; + t->w = w; + t->h = tmp.height; + + scf_logd("t->start: %d, t->len: %d, t->w: %d, t->h: %d\n", t->start, t->len, t->w, t->h); + + *h = t; + h = &t->next; + i += n; + + if (extents->y_bearing > tmp.y_bearing) + extents->y_bearing = tmp.y_bearing; + + extents->height += tmp.height; + break; + } + + n--; + } + } + + return 0; +} + +int abc_layout_text(abc_layout_t* layout, abc_obj_t* obj, int width, int height) +{ + abc_obj_t* parent = obj->parent; + abc_obj_t* attr; + abc_obj_t* style; + cairo_text_extents_t extents; + cairo_text_extents_t extents2; + cairo_surface_t* surface; + cairo_t* cr; + + if (!obj->text) + return 0; + + int x = obj->x - parent->x; + int w = parent->w_set ? parent->w0 : width; + int d = parent->d; + int h; + + scf_logd("obj->x: %d, parent->x: %d, w: %d, d: %d\n", obj->x, parent->x, w, d); + x -= d; + w -= d * 2; + + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, height); + cr = cairo_create(surface); + + int size = 16; + attr = abc_obj_find_attr(obj, ABC_HTML_ATTR_FONT_SIZE); + if (attr) + size = atoi(attr->value->data); + + w = w / size * size; + + scf_logd("text: %s, w: %d\n", obj->text->data, w); + + int ret = __layout_text(cr, obj, x, w, &extents); + if (ret < 0) + scf_loge("ret: %d\n", ret); + + obj->y_bearing = extents.y_bearing; + + if (parent->y_bearing > obj->y_bearing) + parent->y_bearing = obj->y_bearing; + + obj->w = extents.x_bearing + extents.x_advance; + obj->h = extents.height + extents.height / 2; +// obj->w = (obj->w + 3) & ~0x3; +// obj->h = (obj->h + 3) & ~0x3; + + scf_logd("%s, w: %d, h: %d, d: %d, x_bearing: %lg, y_bearing: %lg, width: %lg, height: %lg, x_advance: %lg, y_advance: %lg, width: %d, height: %d\n", + obj->text->data, obj->w, obj->h, d, + extents.x_bearing, extents.y_bearing, extents.width, extents.height, + extents.x_advance, extents.y_advance, width, height); + + cairo_destroy(cr); + cairo_surface_destroy(surface); + return 0; +} diff --git a/ui/abc_render.c b/ui/abc_render.c index bf23041..0e0b02c 100644 --- a/ui/abc_render.c +++ b/ui/abc_render.c @@ -4,9 +4,9 @@ extern abc_render_t abc_render_html; extern abc_render_t abc_render_title; extern abc_render_t abc_render_head; extern abc_render_t abc_render_body; +extern abc_render_t abc_render_text; -extern abc_render_t abc_render_meta; -extern abc_render_t abc_render_div; +extern abc_render_t __render_text; extern abc_render_t abc_render_h1; extern abc_render_t abc_render_a_href; @@ -29,10 +29,14 @@ static abc_render_t* abc_renders[ABC_HTML_NB] = { [ABC_HTML] = &abc_render_empty, [ABC_HTML_HEAD] = &abc_render_empty, - [ABC_HTML_BODY] = &abc_render_body, [ABC_HTML_META] = &abc_render_empty, [ABC_HTML_TITLE] = &abc_render_title, - [ABC_HTML_DIV] = &abc_render_div, + [ABC_HTML_BODY] = &abc_render_body, + [ABC_HTML_DIV] = &abc_render_body, + + [ABC_CORE_TEXT] = &abc_render_text, + [ABC_HTML_B] = &abc_render_text, + [ABC_HTML_I] = &abc_render_text, [ABC_HTML_H1] = &abc_render_h1, [ABC_HTML_H2] = &abc_render_h1, @@ -71,9 +75,6 @@ static abc_render_t* abc_renders[ABC_HTML_NB] = [ABC_HTML_SCRIPT] = &abc_render_empty, [ABC_HTML_STYLE] = &abc_render_empty, [ABC_HTML_LINK] = &abc_render_empty, - - [ABC_HTML_B] = &abc_render_h1, - [ABC_HTML_I] = &abc_render_h1, }; int abc_renders_fini() diff --git a/ui/abc_render_text.c b/ui/abc_render_text.c new file mode 100644 index 0000000..5c77f91 --- /dev/null +++ b/ui/abc_render_text.c @@ -0,0 +1,236 @@ +#include"abc.h" + +static const char* vert_shader = + "#version 330 core\n" + "layout(location = 0) in vec4 position; \n" + "layout(location = 1) in vec2 a_texCoord; \n" + "out vec2 v_texCoord; \n" + "uniform mat4 mvp; \n" + "void main() { \n" + "gl_Position = mvp * position; \n" + "v_texCoord = a_texCoord; \n" + "} \n"; + +static const char* frag_shader = + "#version 330 core\n" + "in vec2 v_texCoord; \n" + "out vec4 outputColor; \n" + "uniform sampler2D tex_rgba; \n" + "void main() { \n" + " vec2 xy = v_texCoord; \n" + " vec4 v = texture2D(tex_rgba, xy).rgba; \n" + " outputColor = vec4(v.b, v.g, v.r, v.a); \n" + "} \n"; + + +static GLuint program = 0; + +static GLuint vao = 0; +static GLuint buffers[2] = {0}; +static GLuint texture_rgba = 0; + +static GLuint uniform_mvp; +static GLuint uniform_rgba; + + +static int _render_fini_text(abc_render_t* render) +{ + return 0; +} + +static int _draw_text_line(abc_obj_t* obj, const char* text, + int x, int y, int w, int h, int width, int height, + float mvp[16], + double bcolor[3], double fcolor[3], int font_size, int bold, int italic, int line_type, int line_width) +{ + uint8_t* bgra = calloc(1, w * h * 4); + if (!bgra) + return -ENOMEM; + + cairo_text_extents_t extents; + cairo_surface_t* surface; + cairo_t* cr; + + surface = cairo_image_surface_create_for_data(bgra, CAIRO_FORMAT_ARGB32, w, h, w * 4); + cr = cairo_create(surface); + + cairo_set_line_width(cr, 1); + cairo_set_source_rgba(cr, bcolor[0], bcolor[1], bcolor[2], 0.0); + cairo_rectangle(cr, 0, 0, w, h); + cairo_fill(cr); + cairo_stroke(cr); + + abc_obj_t* attr = abc_obj_find_attr(obj, ABC_HTML_ATTR_FONT); + if (attr) + cairo_select_font_face(cr, attr->value->data, italic, bold); + + cairo_set_font_size (cr, font_size); + cairo_set_source_rgba(cr, fcolor[0], fcolor[1], fcolor[2], 1.0); + + __draw_text(&extents, cr, text, 0, 0, obj->y_bearing, line_type, line_width); + + // cairo_surface_write_to_png(surface, "tmp.png"); + + cairo_destroy(cr); + cairo_surface_destroy(surface); + surface = NULL; + cr = NULL; + + scf_logd("%s, x: %d, y: %d, w: %d, h: %d\n", obj->text->data, obj->x, obj->y, obj->w, obj->h); + + GLfloat vert_update[] = + { + 2.0 * x / (float)width - 1.0, + -2.0 * (y + h) / (float)height + 1.0, + + 2.0 * (x + w) / (float)width - 1.0, + -2.0 * (y + h) / (float)height + 1.0, + + 2.0 * x / (float)width - 1.0, + -2.0 * y / (float)height + 1.0, + + 2.0 * (x + w) / (float)width - 1.0, + -2.0 * y / (float)height + 1.0, + }; + + glUseProgram(program); + uniform_rgba = glGetUniformLocation(program, "tex_rgba"); + uniform_mvp = glGetUniformLocation(program, "mvp"); + + glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, mvp); + + // board + glActiveTexture(GL_TEXTURE0); + glBindTexture (GL_TEXTURE_2D, texture_rgba); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, bgra); + glUniform1i(uniform_rgba, 0); + + // draw + glBindBuffer (GL_ARRAY_BUFFER, buffers[0]); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vert_update), vert_update); + + glBindVertexArray(vao); + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glDisable(GL_BLEND); + + glBindVertexArray(0); + glUseProgram(0); + + free(bgra); + return 0; +} + +static int _render_draw_text(abc_render_t* render, abc_obj_t* obj, int width, int height) +{ + if (obj->w <= 0 || obj->h <= 0) + return 0; + + if (0 == program) + __init_program(&program, vert_shader, frag_shader); + + if (0 == vao) + __init_buffers(&vao, buffers); + + if (0 == texture_rgba) + __init_texture(&texture_rgba, GL_RGBA, obj->w, obj->h, NULL); + + abc_text_t* t; + abc_obj_t* attr; + + double bcolor[3] = {0.0}; + double fcolor[3] = {0.0}; + + double size = 16.0; + int bold = CAIRO_FONT_WEIGHT_NORMAL; + int italic = CAIRO_FONT_SLANT_NORMAL; + + attr = abc_obj_find_attr(obj, ABC_HTML_ATTR_BG_COLOR); + if (attr) + abc_css_color(bcolor, bcolor + 1, bcolor + 2, attr->value->data); + + attr = abc_obj_find_attr(obj, ABC_HTML_ATTR_FONT_COLOR); + if (attr) + abc_css_color(fcolor, fcolor + 1, fcolor + 2, attr->value->data); + + attr = abc_obj_find_attr(obj, ABC_HTML_ATTR_FONT_SIZE); + if (attr) + size = atoi(attr->value->data); + + attr = abc_obj_find_attr(obj, ABC_HTML_ATTR_FONT_STYLE); + if (attr) { + if (!__html_strcmp(attr->value->data, "bold")) + bold = CAIRO_FONT_WEIGHT_BOLD; + + else if (!__html_strcmp(attr->value->data, "italic") || !__html_strcmp(attr->value->data, "oblique")) + italic = CAIRO_FONT_SLANT_OBLIQUE; + } + + 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) { + if (!__html_strcmp(attr->value->data, "underline")) + line_type = ABC_LINE_UNDER; + + else if (!__html_strcmp(attr->value->data, "overline")) + line_type = ABC_LINE_OVER; + + else if (!__html_strcmp(attr->value->data, "line-through")) + line_type = ABC_LINE_THROUGH; + } + + float mvp[16]; + __compute_mvp(mvp, 0, 0, 0); + + int x = obj->x; + int y = obj->y; + int w = obj->w; + int h = obj->h; + + if (!obj->text_splits) + return _draw_text_line(obj, obj->text->data, + x, y, w, h, width, height, mvp, + bcolor, fcolor, size, bold, italic, line_type, line_width); + + while (obj->text_splits) { + t = obj->text_splits; + + w = t->w; + h = t->h; + + scf_logd("obj->type: %d, obj->w: %d, obj->h: %d, w: %d, h: %d, d: %d\n", obj->type, obj->w, obj->h, w, h, d); + + scf_string_t* 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; + } + + _draw_text_line(obj, s->data, + x, y, w, h, width, height, mvp, + bcolor, fcolor, size, bold, italic, line_type, line_width); + + scf_string_free(s); + s = NULL; + + x = obj->parent->x + obj->parent->d; + y += h; + + obj->text_splits = t->next; + free(t); + } + return 0; +} + +abc_render_t abc_render_text = +{ + .type = ABC_CORE_TEXT, + + .draw = _render_draw_text, + .fini = _render_fini_text, +};