css: support simple combinators with pse class or pse element
authoryu.dongliang <18588496441@163.com>
Wed, 13 May 2026 10:58:36 +0000 (18:58 +0800)
committeryu.dongliang <18588496441@163.com>
Wed, 13 May 2026 10:58:36 +0000 (18:58 +0800)
17 files changed:
examples/div.html [new file with mode: 0644]
examples/p_group.html [new file with mode: 0644]
examples/p_pse_class.html [new file with mode: 0644]
html/abc_css.c
html/abc_css_color.c
html/abc_html.c
html/abc_obj.c
html/abc_obj.h
ui/Makefile
ui/__render_text.c
ui/abc.h
ui/abc_layout.c
ui/abc_layout_div.c
ui/abc_layout_h1.c
ui/abc_layout_text.c [new file with mode: 0644]
ui/abc_render.c
ui/abc_render_text.c [new file with mode: 0644]

diff --git a/examples/div.html b/examples/div.html
new file mode 100644 (file)
index 0000000..bf86a04
--- /dev/null
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+div {
+    background-color: lightgrey;
+    width: 300px;
+    border: 25px solid green;
+    padding: 25px;
+    margin: 25px;
+}
+</style>
+</head>
+
+<body>
+<h2>盒子模型演示</h2>
+
+<p>CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距,边框,填充,和实际内容。</p>
+
+<div>这里是盒子内的实际内容。有 25px 内间距,<b>25px 外间距</b>、25px 绿色边框。</div>
+</body>
+</html>
diff --git a/examples/p_group.html b/examples/p_group.html
new file mode 100644 (file)
index 0000000..535de1d
--- /dev/null
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"> 
+<title>菜鸟教程(runoob.com)</title> 
+<style>
+p
+{
+       color:blue;
+       text-align:center;
+}
+.marked 
+{
+       background-color:red;
+}
+.marked p, p.marked
+{
+       color:white;
+}
+p.marked { text-decoration:underline;}
+</style>
+</head>
+
+<body>
+<p>这个段落是蓝色文本,居中对齐。</p>
+<div class="marked">
+<p>这个段落不是蓝色文本。</p>
+</div>
+<p>所有 class="marked"元素内的 p 元素指定一个样式,但有不同的文本颜色。</p>
+
+<p class="marked">带下划线的 p 段落。</p>
+</body>
+</html>
diff --git a/examples/p_pse_class.html b/examples/p_pse_class.html
new file mode 100644 (file)
index 0000000..2e1e5ea
--- /dev/null
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"> 
+<style>
+p > b:first-child
+{
+       color:blue;
+}
+
+p:first-child b
+{
+       color:red;
+}
+</style>
+</head>
+
+<body>
+<p>I am a <b>strong</b> man. I am a <b>strong</b> man.</p>
+<p>I am a <b>strong</b> man. I am a <b>strong</b> man.</p>
+<p><b>注意:</b> 当 :first-child 作用于 IE8 以及更早版本的浏览器, !DOCTYPE 必须已经定义.</p>
+</body>
+</html>
index fe9ca4226af15b114e31b02b9f8ebf502a7739ac..cb71c3d11d6ee45b9be5053889651c1e6fdff765 100644 (file)
@@ -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(&current->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(&current->list);
+                       if (l == scf_list_sentinel(&current->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(&current->list); l != scf_list_sentinel(&current->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(&current->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(&current->list);
+                       if (l == scf_list_sentinel(&current->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(&current->list); l != scf_list_sentinel(&current->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(&current->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
index e2c29453c9b42ac026b5f7d337e29e42f0d6981c..330474faa38c3f1922d8d0f619154bfe7b211cbd 100644 (file)
@@ -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};
index bd4f58b8c9a011ba123bad10ac6ac927100ab2c7..f2f27158aee1cbe0a66f902302b0559574a486b7 100644 (file)
@@ -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);
index ac1beafed2e678dba5727ed8ea27188b6537d142..91c39d216bc63c70e1b733a06247f6818730207d 100644 (file)
@@ -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, &copy->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);
index 787dacd5005204b32211d31b2befc88632ab9600..85a8b5124e47b00a4cf47f33dfb01b26eaba4438 100644 (file)
@@ -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);
 
index e95b12431f0fc76ce70e97f1aff05a697f6b2cdb..0bb821b765da7b629d481d5516b8b225813ab309 100644 (file)
@@ -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
 
index 6ca9a8abf97facace9e50fd855eb5469953857fa..609e4bc09f5719e45d52253436895bbcdf745597 100644 (file)
@@ -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;
index ba523a1f1911dee20799fb29502f0323aa190587..50174ee2c532bac3b981b4c7ed1332d7cb41db62 100644 (file)
--- 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;
index 1dd4fd5a810a5e620c5625995c170982c29685ce..a7e7796a784a4d993af31fb6bcaf04d299aaff24 100644 (file)
@@ -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)
index 6878cc0076dfcce4a249a3b17719998209412ebc..3fd9d7ccbdbc942aada5b01bd1ceaabf87078a84 100644 (file)
@@ -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;
 }
index a620318d5f4fb3b110041c61e97eefc52399ba86..981b11edae671087812683261444ae32880a87e7 100644 (file)
 #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 (file)
index 0000000..bc955bb
--- /dev/null
@@ -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;
+}
index bf23041d4b7d25b9dcfe678ff0573752e75676d4..0e0b02c4c42e5678adbdc563f041b35e534140b2 100644 (file)
@@ -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 (file)
index 0000000..5c77f91
--- /dev/null
@@ -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,
+};