commit 6d48b29c2bc7ce93eb6ac30f594bdcfb3aa97e11 Author: James Bursa Date: Mon Apr 22 09:24:35 2002 +0000 [project @ 2002-04-22 09:24:35 by bursa] Initial revision svn path=/import/netsurf/; revision=2 diff --git a/render/css.c b/render/css.c new file mode 100644 index 000000000..45ed2d9bd --- /dev/null +++ b/render/css.c @@ -0,0 +1,545 @@ +/** + * $Id: css.c,v 1.1.1.1 2002/04/22 09:24:34 bursa Exp $ + */ + +#include +#include +#include +#include "css.h" +#include "utils.h" + +/** + * internal structures + */ + +struct rule { + struct css_selector * selector; + unsigned int selectors; + struct css_style * style; + struct rule * next; +}; + +struct decl { + unsigned long score; + struct rule * rule; +}; + +#define HASH_SIZE 13 + +struct css_stylesheet { + struct rule * hash[HASH_SIZE]; +}; + +static int parse_length(struct css_length * const length, const char *s); +static void parse_display(struct css_style * const style, const char * const value); +static void parse_float(struct css_style * const style, const char * const value); +static void parse_font_size(struct css_style * const style, const char * const value); +static void parse_height(struct css_style * const style, const char * const value); +static void parse_width(struct css_style * const style, const char * const value); +static void parse_property_list(struct css_style * style, char * str); +static void parse_selector(struct css_selector * sel, char * const str); +static unsigned int hash_str(const char * str); +static int seleq(const struct css_selector * const s1, const struct css_selector * const s2); +static unsigned long selmatch(const struct css_selector * const s, const struct css_selector * const sr); +static struct rule * find_rule(struct css_stylesheet * stylesheet, + struct css_selector * selector, unsigned int selectors); +static int cmpdecl(const struct decl * d0, const struct decl * d1); +static void update_style(struct css_stylesheet * stylesheet, struct css_selector * selector, + unsigned int selectors, char * str); +static void dump_length(const struct css_length * const length); +static void dump_selector(const struct css_selector * const sel); +static void dump_rule(const struct rule * rule); +static void css_dump_stylesheet(const struct css_stylesheet * stylesheet); + +/** + * property parsers + */ + +static int parse_length(struct css_length * const length, const char *s) +{ + length->unit = css_unit_parse(s + strspn(s, "0123456789+-.")); + if (length->unit == CSS_UNIT_UNKNOWN) return 1; + length->value = atof(s); + return 0; +} + +static void parse_display(struct css_style * const style, const char * const value) +{ + style->display = css_display_parse(value); +} + +static void parse_float(struct css_style * const style, const char * const value) +{ + style->float_ = css_float_parse(value); +} + +#define SIZE_FACTOR 1.2 + +static struct font_size { + const char * keyword; + float size; +} const font_size[] = { + { "xx-small", 1.0 / (SIZE_FACTOR * SIZE_FACTOR * SIZE_FACTOR) }, + { "x-small", 1.0 / (SIZE_FACTOR * SIZE_FACTOR) }, + { "small", 1.0 / SIZE_FACTOR }, + { "medium", 1.0 }, + { "large", 1.0 * SIZE_FACTOR }, + { "x-large", 1.0 * SIZE_FACTOR * SIZE_FACTOR }, + { "xx-large", 1.0 * SIZE_FACTOR * SIZE_FACTOR * SIZE_FACTOR } +}; + +static void parse_font_size(struct css_style * const style, const char * const value) +{ + unsigned int i; + for (i = 0; i < sizeof(font_size) / sizeof(struct font_size); i++) { + if (strcmp(value, font_size[i].keyword) == 0) { + style->font_size.size = CSS_FONT_SIZE_ABSOLUTE; + style->font_size.value.absolute = font_size[i].size; + return; + } + } + if (strcmp(value, "larger") == 0) + style->font_size.size = CSS_FONT_SIZE_PERCENT, + style->font_size.value.percent = SIZE_FACTOR * 100; + else if (strcmp(value, "smaller") == 0) + style->font_size.size = CSS_FONT_SIZE_PERCENT, + style->font_size.value.percent = 1 / SIZE_FACTOR * 100; + else if (strrchr(value, '%')) + style->font_size.size = CSS_FONT_SIZE_PERCENT, + style->font_size.value.percent = atof(value); + else if (parse_length(&style->font_size.value.length, value) == 0) + style->font_size.size = CSS_FONT_SIZE_LENGTH; +} + +static void parse_height(struct css_style * const style, const char * const value) +{ + if (strcmp(value, "auto") == 0) + style->height.height = CSS_HEIGHT_AUTO; + else if (parse_length(&style->height.length, value) == 0) + style->height.height = CSS_HEIGHT_LENGTH; +} + +static void parse_width(struct css_style * const style, const char * const value) +{ + if (strcmp(value, "auto") == 0) + style->width.width = CSS_WIDTH_AUTO; + else if (strrchr(value, '%')) + style->width.width = CSS_WIDTH_PERCENT, + style->width.value.percent = atof(value); + else if (parse_length(&style->width.value.length, value) == 0) + style->width.width = CSS_WIDTH_LENGTH; +} + +static struct property { + const char * const name; + void (*parse) (struct css_style * const s, const char * const value); +} const property[] = { + { "display", parse_display }, + { "float", parse_float }, + { "font-size", parse_font_size }, + { "height", parse_height }, + { "width", parse_width }, +}; + +/** + * parse a property list + */ + +static void parse_property_list(struct css_style * style, char * str) +{ + char * end; + for (; str != 0; str = end) { + char * prop; + char * value; + unsigned int i; + + if ((end = strchr(str, ';')) != 0) *end = 0, end++; + if ((value = strchr(str, ':')) == 0) continue; + *value = 0; value++; + prop = strip(str); + value = strip(value); + printf("css_parse: '%s' => '%s'\n", prop, value); + + for (i = 0; i < sizeof(property) / sizeof(struct property); i++) { + if (strcmp(prop, property[i].name) == 0) { + property[i].parse(style, value); + break; + } + } + } +} + +/** + * selectors + */ + +static void parse_selector(struct css_selector * sel, char * const str) +{ + char * dot = strchr(str, '.'); + char * hash = strchr(str, '#'); + + if ((dot == 0) && (hash == 0)) { + sel->element = xstrdup(str); + sel->class = sel->id = 0; + } else if (dot != 0) { + *dot = 0; + sel->element = xstrdup(str); + sel->class = xstrdup(dot + 1); + sel->id = 0; + } else if (hash != 0) { + *hash = 0; + sel->element = xstrdup(str); + sel->class = 0; + sel->id = xstrdup(hash + 1); + } +} + +/** + * stylesheet structure + */ + +static unsigned int hash_str(const char * str) +{ + unsigned int s = 0; + for (; *str != 0; str++) + s += *str; + return s % HASH_SIZE; +} + +static int seleq(const struct css_selector * const s1, const struct css_selector * const s2) +{ + return strcmp(s1->element, s2->element) == 0 && + ((s1->class == 0 && s2->class == 0) || + (s1->class != 0 && s2->class != 0 && strcmp(s1->class, s2->class) == 0)) && + ((s1->id == 0 && s2->id == 0) || + (s1->id != 0 && s2->id != 0 && strcmp(s1->id, s2->id) == 0)); +} + +static unsigned long selmatch(const struct css_selector * const s, const struct css_selector * const sr) +{ + unsigned int c; + if (strcmp(s->element, sr->element) != 0) return 0; + c = s->element[0] == 0 ? 0 : 1; + if (sr->class != 0) { + if (s->class != 0 && strcmp(s->class, sr->class) == 0) return 0x100 + c; + return 0; + } + if (sr->id != 0) { + if (s->id != 0 && strcmp(s->id, sr->id) == 0) return 0x10000 + c; + return 0; + } + return 1; +} + +struct css_stylesheet * css_new_stylesheet(void) +{ + struct css_stylesheet * stylesheet = xcalloc(1, sizeof(struct css_stylesheet)); + return stylesheet; +} + +static struct rule * find_rule(struct css_stylesheet * stylesheet, + struct css_selector * selector, unsigned int selectors) +{ + struct rule * rule; + for (rule = stylesheet->hash[hash_str(selector[selectors - 1].element)]; + rule != 0; rule = rule->next) { + unsigned int i; + if (selectors != rule->selectors) + continue; + /* inv: selector[0..i) == rule->selector[0..i) */ + for (i = 0; i != selectors && seleq(&selector[i], &rule->selector[i]); i++) + ; + if (i == selectors) + return rule; + } + return 0; +} + +static int cmpdecl(const struct decl * d0, const struct decl * d1) +{ + if (d0->score < d1->score) return -1; + else if (d0->score == d1->score) return 0; + return 1; +} + +void css_get_style(struct css_stylesheet * stylesheet, struct css_selector * selector, + unsigned int selectors, struct css_style * style) +{ + struct rule * rule; + struct decl * decl = xcalloc(0, sizeof(struct decl)); + unsigned int d, decls = 0; + + for (rule = stylesheet->hash[hash_str(selector[selectors - 1].element)]; + rule != 0; rule = rule->next) { + unsigned int i = selectors - 1; + unsigned int j; + unsigned int score, s; + + if ((score = selmatch(&selector[i], &rule->selector[rule->selectors - 1])) == 0) + continue; + if (selectors < rule->selectors) + continue; + + for (j = rule->selectors - 1; j != 0; j--) { + for (; i != 0 && (s = selmatch(&selector[i - 1], &rule->selector[j - 1])) == 0; i--) + ; + if (i == 0) + break; + score += s; + } + if (j == 0) { + decl = xrealloc(decl, (decls + 1) * sizeof(struct decl)); + decl[decls].score = score; + decl[decls].rule = rule; + decls++; + } + } + + qsort(decl, decls, sizeof(struct decl), (int (*) (const void *, const void *)) cmpdecl); + + for (d = 0; d < decls; d++) { +/* printf("%i: 0x%lx\n", d, decl[d].score); */ +/* css_dump_rule(decl[d].rule); */ + css_cascade(style, decl[d].rule->style); + } +} + +static void update_style(struct css_stylesheet * stylesheet, struct css_selector * selector, + unsigned int selectors, char * str) +{ + struct rule * rule = find_rule(stylesheet, selector, selectors); + if (rule == 0) { + unsigned int h = hash_str(selector[selectors - 1].element); + printf("update_style: not present - adding\n"); + rule = xcalloc(1, sizeof(struct rule)); + rule->selector = selector; + rule->selectors = selectors; + rule->style = xcalloc(1, sizeof(struct css_style)); + parse_property_list(rule->style, str); + rule->next = stylesheet->hash[h]; + stylesheet->hash[h] = rule; + } else { + printf("update_style: already present - updating\n"); + parse_property_list(rule->style, str); + free(selector); + } +} + +/** + * parse an entire css file or block + */ + +void css_parse_stylesheet(struct css_stylesheet * stylesheet, char * str) +{ + /* overwrite comments with spaces */ + char * copen = strstr(str, "/*"); + while (copen != 0) { + char * cclose = strstr(copen + 2, "*/"); + if (cclose == 0) { *copen = 0; break; } + memset(copen, ' ', (size_t) (cclose - copen + 2)); + copen = strstr(cclose + 2, "/*"); + } + + while (*str != 0) { + char * open = strchr(str, '{'); + char * close = strchr(str, '}'); + char * sels_str; + char * comma; + + if ((open == 0) || (close == 0)) break; + *open = 0; *close = 0; + + sels_str = strip(str); + do { + char * sel_str; + char * space; + char * style_str; + unsigned int selectors = 0; + struct css_selector * selector = xcalloc(0, sizeof(struct css_selector)); + + comma = strchr(sels_str, ','); + if (comma != 0) *comma = 0; + + sel_str = strip(sels_str); + printf("css_parse_stylesheet: %s\n", sel_str); + do { + space = strchr(sel_str, ' '); + if (space != 0) *space = 0; + selector = xrealloc(selector, sizeof(struct css_selector) * (selectors + 1)); + parse_selector(&selector[selectors++], sel_str); + if (space != 0) sel_str = strip(space + 1); + } while (space != 0); + + style_str = xstrdup(open + 1); + update_style(stylesheet, selector, selectors, style_str); + free(style_str); + + if (comma != 0) sels_str = strip(comma + 1); + } while (comma != 0); + + str = close + 1; + } +} + +/** + * dump a style + */ + +static void dump_length(const struct css_length * const length) +{ + printf("%g%s", length->value, + css_unit_name[length->unit]); +} + +void css_dump_style(const struct css_style * const style) +{ + puts("{"); + printf("\tdisplay: %s;\n", css_display_name[style->display]); + printf("\tfloat: %s;\n", css_float_name[style->float_]); + printf("\tfont-size: "); + switch (style->font_size.size) { + case CSS_FONT_SIZE_ABSOLUTE: printf("[%g]", style->font_size.value.absolute); break; + case CSS_FONT_SIZE_LENGTH: dump_length(&style->font_size.value.length); break; + case CSS_FONT_SIZE_PERCENT: printf("%g%%", style->font_size.value.percent); break; + case CSS_FONT_SIZE_INHERIT: printf("inherit"); break; + default: printf("UNKNOWN"); break; + } + puts(";"); + printf("\theight: "); + switch (style->height.height) { + case CSS_HEIGHT_AUTO: printf("auto"); break; + case CSS_HEIGHT_LENGTH: dump_length(&style->height.length); break; + default: printf("UNKNOWN"); break; + } + puts(";"); + printf("\twidth: "); + switch (style->width.width) { + case CSS_WIDTH_AUTO: printf("auto"); break; + case CSS_WIDTH_LENGTH: dump_length(&style->width.value.length); break; + case CSS_WIDTH_PERCENT: printf("%g%%", style->width.value.percent); break; + default: printf("UNKNOWN"); break; + } + puts(";"); + puts("}"); +} + +static void dump_selector(const struct css_selector * const sel) +{ + if (sel->class != 0) + printf("%s.%s ", sel->element, sel->class); + else if (sel->id != 0) + printf("%s#%s ", sel->element, sel->id); + else + printf("%s ", sel->element); +} + +static void dump_rule(const struct rule * rule) +{ + unsigned int i; + for (i = 0; i < rule->selectors; i++) + dump_selector(&rule->selector[i]); + css_dump_style(rule->style); +} + +static void css_dump_stylesheet(const struct css_stylesheet * stylesheet) +{ + unsigned int i; + for (i = 0; i < HASH_SIZE; i++) { + struct rule * rule; + printf("hash %i:\n", i); + for (rule = stylesheet->hash[i]; rule != 0; rule = rule->next) + dump_rule(rule); + } +} + +/** + * cascade styles + */ + +void css_cascade(struct css_style * const style, const struct css_style * const apply) +{ + float f; + style->display = apply->display; + style->float_ = apply->float_; + style->height = apply->height; + style->width = apply->width; + + /* font-size */ + f = apply->font_size.value.percent / 100; + switch (apply->font_size.size) { + case CSS_FONT_SIZE_ABSOLUTE: style->font_size = apply->font_size; break; + case CSS_FONT_SIZE_LENGTH: + switch (apply->font_size.value.length.unit) { + case CSS_UNIT_EM: f = apply->font_size.value.length.value; break; + case CSS_UNIT_EX: f = apply->font_size.value.length.value * 0.6 /*?*/; break; + default: style->font_size = apply->font_size; + } + if ((apply->font_size.value.length.unit != CSS_UNIT_EM) && + (apply->font_size.value.length.unit != CSS_UNIT_EX)) break; + /* drop through if EM or EX */ + case CSS_FONT_SIZE_PERCENT: + switch (style->font_size.size) { + case CSS_FONT_SIZE_ABSOLUTE: style->font_size.value.absolute *= f; break; + case CSS_FONT_SIZE_LENGTH: style->font_size.value.length.value *= f; break; + default: die("attempting percentage of unknown font-size"); + } + break; + case CSS_FONT_SIZE_INHERIT: + default: /* leave unchanged */ + } +} + +/** + * testing + */ +/* +int main(int argv, char *argc[]) +{ + int i; + struct rule * r; + struct css_stylesheet * s; + struct css_selector sel = { "h1", 0, 0 }; + struct css_selector con[] = {{ "html", 0, 0 }, {"body", 0, 0}, {"h1", "foo", 0}, {"b", 0, "bar"}}; + struct css_style * style = xcalloc(1, sizeof(struct css_style)); + + s = css_new_stylesheet(); + css_parse_stylesheet(s, argc[1]); + css_dump_stylesheet(s); + +/* r->selectors = 1; */ +/* css_stylesheet_add_rule(s, r); */ +/* puts("********** finding h1:"); + r = find_rule(s, &sel, 1); + if (r) + dump_rule(r); + else + puts("not found"); + + puts("********** finding html body h1.foo b#bar:"); + for (i = 1; i <= sizeof(con) / sizeof(con[0]); i++) { + css_get_style(s, con, i, style); + css_dump_style(style); + } + +/* printf("%x %x\n", r, r2); */ + +/* struct css_style *s; + struct css_selector *sel; + + s = parse_property_list(argc[1]); + css_dump_style(s); + + for (i = 2; i < argv; i++) { + css_cascade(s, parse_property_list(argc[i])); + css_dump_style(s); + }*/ + +/* for (i = 1; i < argv; i++) { + sel = parse_selector(argc[i]); + css_dump_selector(sel); + puts(""); + }*/ +/* + return 0; +} +*/ diff --git a/render/css.h b/render/css.h new file mode 100644 index 000000000..3e465fde1 --- /dev/null +++ b/render/css.h @@ -0,0 +1,87 @@ +/** + * $Id: css.h,v 1.1.1.1 2002/04/22 09:24:34 bursa Exp $ + */ + +#include "css_enum.h" + +/** + * structures and typedefs + */ + +typedef unsigned long colour; /* 0xrrggbb */ +#define TRANSPARENT 0x1000000 + +struct css_length { + float value; + css_unit unit; +}; + +struct css_style { + css_display display; + css_float float_; + + struct { + enum { CSS_FONT_SIZE_INHERIT, + CSS_FONT_SIZE_ABSOLUTE, + CSS_FONT_SIZE_LENGTH, + CSS_FONT_SIZE_PERCENT } size; + union { + float absolute; + struct css_length length; + float percent; + } value; + } font_size; + + struct { + enum { CSS_HEIGHT_AUTO, + CSS_HEIGHT_LENGTH } height; + struct css_length length; + } height; + + struct { + enum { CSS_WIDTH_AUTO, + CSS_WIDTH_LENGTH, + CSS_WIDTH_PERCENT } width; + union { + struct css_length length; + float percent; + } value; + } width; + + + enum { BACKGROUND_SCROLL = 1, BACKGROUND_FIXED } background_attachment; + colour background_color; + /* char background_image[100]; */ + /* background-position */ + enum { BACKGROUND_REPEAT = 1, BACKGROUND_REPEAT_X, + BACKGROUND_REPEAT_Y, BACKGROUND_NO_REPEAT } background_repeat; + /* borders */ + enum { CLEAR_NONE = 1, CLEAR_BOTH, CLEAR_LEFT, CLEAR_RIGHT } clear; + colour color; + /* font-family */ + enum { FONT_STRAIGHT, FONT_OBLIQUE, FONT_ITALIC } font_style; + enum { FONT_NORMAL, FONT_SMALLCAPS } font_variant; + struct { + enum { WEIGHT_ABSOLUTE, WEIGHT_BOLDER, WEIGHT_LIGHTER } weight; + unsigned int value; + } font_weight; +}; + +struct css_stylesheet; + +struct css_selector { + char * element; + char * class; + char * id; +}; + +/** + * interface + */ + +struct css_stylesheet * css_new_stylesheet(void); +void css_get_style(struct css_stylesheet * stylesheet, struct css_selector * selector, + unsigned int selectors, struct css_style * style); +void css_parse_stylesheet(struct css_stylesheet * stylesheet, char * str); +void css_dump_style(const struct css_style * const style); +void css_cascade(struct css_style * const style, const struct css_style * const apply); diff --git a/render/css_enums b/render/css_enums new file mode 100644 index 000000000..8ef3ffd3b --- /dev/null +++ b/render/css_enums @@ -0,0 +1,22 @@ +css_unit em ex px in cm mm pt pc +css_background_attachment inherit fixed scroll +css_background_position inherit top center bottom left right length percent +css_background_repeat inherit repeat repeat_x repeat_y no_repeat +css_border_width inherit medium thin thick length +css_border_style inherit none dashed dotted double groove inset outset ridge solid +css_clear none both left right +css_display block inline none +css_float none left right +css_font_style normal italic oblique +css_font_variant normal smallcaps +css_font_weight normal bold bolder lighter 100 200 300 400 500 600 700 800 900 +css_letter_spacing normal length +css_line_height normal length number percent +css_list_style_position outside inside +css_list_style_type disc circle square decimal lower_alpha lower_roman upper_alpha upper_roman none +css_margin auto length percent +css_text_align left right center justify +css_text_decoration none blink line_through overline underline +css_text_transform none capitalize lowercase uppercase +css_vertical_align baseline bottom middle sub super text_bottom text_top top percent +css_white_space normal nowrap pre diff --git a/render/makeenum b/render/makeenum new file mode 100644 index 000000000..57f3375d1 --- /dev/null +++ b/render/makeenum @@ -0,0 +1,36 @@ +#!/usr/bin/perl -W +# $Id: makeenum,v 1.1.1.1 2002/04/22 09:24:34 bursa Exp $ + +$out = shift or die "usage: makeenum leafname"; + +open H, ">$out.h" or die "open 'enum.h' failed"; +open C, ">$out.c" or die "open 'enum.c' failed"; + +print C "#include \"$out.h\"\n\n"; + +while (<>) { + chomp; + @enum = split; + $name = shift @enum; + + @uc_enum = map uc, @enum; + $uc_name = uc $name; + + print H "extern const char * const ${name}_name[];\n"; + print H "typedef enum {\n ${uc_name}_"; + print H join ",\n ${uc_name}_", @uc_enum; + print H ",\n ${uc_name}_UNKNOWN\n"; + print H "} $name;\n"; + print H "$name ${name}_parse(const char * const s);\n\n"; + + print C "/**\n * $name\n */\n\n"; + print C "const char * const ${name}_name[] = {\n \""; + print C join "\",\n \"", @enum; + print C "\"\n};\n\n"; + print C "$name ${name}_parse(const char * const s)\n{\n"; + foreach $x (@enum) { + $ux = uc $x; + print C " if (strcmp(s, \"$x\") == 0) return ${uc_name}_$ux;\n"; + } + print C " return ${uc_name}_UNKNOWN;\n}\n\n"; +} diff --git a/render/makefile b/render/makefile new file mode 100644 index 000000000..5630b6a33 --- /dev/null +++ b/render/makefile @@ -0,0 +1,24 @@ +# $Id: makefile,v 1.1.1.1 2002/04/22 09:24:34 bursa Exp $ + +FLAGS = -g -Wall -W -Wundef -Wshadow -Wpointer-arith -Wbad-function-cast -Wcast-qual \ +-Wcast-align -Wwrite-strings -Wconversion -Wstrict-prototypes -Wmissing-prototypes \ +-Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -std=c9x +CC = gcc + +render: render.o utils.o css.o css_enum.o + $(CC) $(FLAGS) `xml2-config --libs` -o render render.o utils.o css.o css_enum.o + +render.o: render.c css.h css_enum.h utils.h + $(CC) $(FLAGS) `xml2-config --cflags` -c render.c + +css.o: css.c css.h css_enum.h utils.h + $(CC) $(FLAGS) -c css.c + +utils.o: utils.c utils.h + $(CC) $(FLAGS) -c utils.c + +css_enum.o: css_enum.c css_enum.h + $(CC) $(FLAGS) -c css_enum.c + +css_enum.c css_enum.h: css_enums makeenum + ./makeenum css_enum < css_enums diff --git a/render/render.c b/render/render.c new file mode 100644 index 000000000..2b913e6ec --- /dev/null +++ b/render/render.c @@ -0,0 +1,484 @@ +/** + * $Id: render.c,v 1.1.1.1 2002/04/22 09:24:35 bursa Exp $ + */ + +#include +#include +#include +#include +#include +#include "parser.h" /* libxml */ +#include "css.h" +#include "utils.h" + +/** + * internal structures + */ + +struct coord { + unsigned long x, y; +}; + +struct data { /* used in _private field of xmlNode */ + struct css_style * style; + unsigned long x, y, width, height; +}; + +struct box { + enum { BOX_BLOCK, BOX_INLINE, BOX_FLOAT } type; + enum { CONTENT_BLOCK, CONTENT_INLINE } content; + xmlNode * node; + struct css_style * style; + unsigned long x, y, width, height; + const char * text; + unsigned int length; + struct box * next; + struct box * children; + struct box * parent; +}; + +void layout_element(xmlNode * e, unsigned long width); +unsigned long layout_element_children(xmlNode * e, unsigned long width); + +/** + * convert a struct css_length to pixels + */ + +signed long len(struct css_length * length, unsigned long em) +{ + switch (length->unit) { + case CSS_UNIT_EM: return length->value * em; + case CSS_UNIT_EX: return length->value * em * 0.6; + case CSS_UNIT_PX: return length->value; + case CSS_UNIT_IN: return length->value * 90.0; + case CSS_UNIT_CM: return length->value * 35.0; + case CSS_UNIT_MM: return length->value * 3.5; + case CSS_UNIT_PT: return length->value * 90.0 / 72.0; + case CSS_UNIT_PC: return length->value * 90.0 / 6.0; + default: return 0; + } + return 0; +} + +/** + * layout algorithm + */ + +void layout_element(xmlNode * e, unsigned long width) +{ + struct data * data = (struct data *) e->_private; + struct css_style * style = data->style; + switch (style->width.width) { + case CSS_WIDTH_AUTO: + data->width = width; + break; + case CSS_WIDTH_LENGTH: + data->width = len(&style->width.value.length, 10); + break; + case CSS_WIDTH_PERCENT: + data->width = width * style->width.value.percent / 100; + break; + } + data->height = layout_element_children(e, data->width); + switch (style->height.height) { + case CSS_HEIGHT_AUTO: + break; + case CSS_HEIGHT_LENGTH: + data->height = len(&style->height.length, 10); + break; + } +} + +unsigned long layout_element_children(xmlNode * e, unsigned long width) +{ + struct coord pos; + int inline_mode = 0; + xmlNode * c = e->children; + xmlNode * next; + unsigned long y = 0; + struct coord float_left = { 0, 0 }, float_right = { 0, 0 }; /* bottom corner of current float */ + xmlNode * line; /* first node in current line box */ + + printf("layout_element_children: starting %s\n", e->name); + + while (c != 0) { + struct data * data = (struct data *) c->_private; + next = c->next; + switch (c->type) { + case XML_ELEMENT_NODE: { + struct css_style * style = data->style; + printf("element %s: ", c->name); + switch (style->float_) { + case CSS_FLOAT_NONE: + switch (style->display) { + case CSS_DISPLAY_BLOCK: + printf("block"); + if (inline_mode) { + y = pos.y; + inline_mode = 0; + printf(" (inline_mode = 0)"); + } + puts(""); + layout_element(c, width); + data->x = 0; + data->y = y; + y += data->height; + break; + case CSS_DISPLAY_INLINE: + puts("inline"); + next = c->children; + /* TODO: fill x, y, width, height [1] */ + /* TODO: replaced elements */ + break; + } + break; + case CSS_FLOAT_LEFT: + puts("float left"); + layout_element(c, width); + data->x = 0; + if (inline_mode) { + if (data->width <= width - pos.y) { + xmlNode * n; + for (n = line; n != c; + n = n->next ? n->next : n->parent->next) { + printf("moving %s\n", n->name); + if (n->_private) + ((struct data *) n->_private)->x += + data->width; + } + data->y = y; + } else { + data->y = pos.y; + } + } else { + data->y = y; + } + float_left.x = data->width; + float_left.y = data->y + data->height; + break; + case CSS_FLOAT_RIGHT: + puts("float right"); + layout_element(c, width); + data->x = width - data->width; + if (inline_mode) { + if (data->width <= width - pos.y) { + data->y = y; + } else { + data->y = pos.y; + } + } else { + data->y = y; + } + float_right.x = data->x; + float_right.y = data->y + data->height; + break; + } + break; + } + case XML_TEXT_NODE: + printf("text: "); + if (whitespace(c->content)) { + c->_private = 0; + puts("whitespace"); + } else { + struct data * data = xcalloc(1, sizeof(struct data)); + unsigned int x1 = y < float_right.y ? float_right.x : width; + if (!inline_mode) { + pos.x = y < float_left.y ? float_left.x : 0; + pos.y = y; + inline_mode = 1; + line = c; + printf("(inline_mode = 1)"); + } + puts(""); + c->_private = data; + data->height = 2; + data->width = strlen(c->content) + 1; + /* space available is pos.x to x1 */ + if (x1 - pos.x < data->width) { + /* insufficient space: start new line */ + y = pos.y; + pos.x = y < float_left.y ? float_left.x : 0; + line = c; + } + data->x = pos.x; + data->y = y; + pos.x += data->width; + pos.y = y + 2; + } + break; + } + while (next == 0 && c->parent != e) + /* TODO: fill coords of just finished inline element [1] */ + c = c->parent, next = c->next; + c = next; + } + if (inline_mode) y = pos.y; + return y; +} + + +/******************************************************************************/ + + +void render_plain_element(char *g, xmlNode *e, unsigned long x, unsigned long y) +{ + unsigned long i; + unsigned int l; + xmlNode *c; + struct data * data = e->_private; + + for (c = e->children; c != 0; c = c->next) + render_plain_element(g, c, x + data->x, y + data->y); + + if (data == 0) return; + +// printf("render_plain_element: x0 %li y0 %li x1 %li y1 %li\n", data->x0, data->y0, data->x1, data->y1); + + for (i = (y + data->y) + 1; i < (y + data->y + data->height); i++) { + g[80 * i + (x + data->x)] = '|'; + g[80 * i + (x + data->x + data->width)] = '|'; + } + +// if (e->style->display != INLINE) { + for (i = (x + data->x); i < (x + data->x + data->width); i++) { + g[80 * (y + data->y) + i] = '-'; + g[80 * (y + data->y + data->height) + i] = '-'; + } + g[80 * (y + data->y) + (x + data->x)] = '+'; + g[80 * (y + data->y) + (x + data->x + data->width)] = '+'; + g[80 * (y + data->y + data->height) + (x + data->x)] = '+'; + g[80 * (y + data->y + data->height) + (x + data->x + data->width)] = '+'; +// } + + if (e->type == XML_TEXT_NODE && e->content) { + l = strlen(e->content); + if ((x + data->x + data->width) - (x + data->x) - 1 < l) + l = (x + data->x + data->width) - (x + data->x) - 1; + strncpy(g + 80 * ((y + data->y) + 1) + (x + data->x) + 1, e->content, l); + } +} + + +void render_plain(xmlNode *doc) +{ + int i; + char *g; + + g = calloc(10000, 1); + if (g == 0) exit(1); + + for (i = 0; i < 10000; i++) + g[i] = ' '; + + render_plain_element(g, doc, 0, 0); + + for (i = 0; i < 40; i++) + printf("%.80s\n", g + (80 * i)); +} + + +/******************************************************************************/ + + +void walk(xmlNode *n, unsigned int depth) +{ + xmlNode *c; + xmlAttr *a; + struct data * data; + unsigned int i; + + for (i = 0; i < depth; i++) + printf(" "); + + data = n->_private; + + switch (n->type) { + case XML_ELEMENT_NODE: + if (data == 0) + printf("ELEMENT %s", n->name); + else + printf("ELEMENT %s [%li %li %li*%li]", n->name, data->x, + data->y, data->width, data->height); + /* for (a = n->properties; a != 0; a = a->next) { + assert(a->type == XML_ATTRIBUTE_NODE); + printf(" %s='", a->name); + for (c = a->children; c != 0; c = c->next) + walk(c); + printf("'"); + }*/ + printf("\n"); + for (c = n->children; c != 0; c = c->next) + walk(c, depth + 1); +// printf("", n->name); + break; + + case XML_TEXT_NODE: + if (data == 0) + printf("TEXT '%s'\n", n->content); + else + printf("TEXT [%li %li %li*%li] '%s'\n", data->x, data->y, + data->width, data->height, n->content); + break; + + default: + printf("UNHANDLED\n"); + break; + } +} + + + +/** + * make a box tree with style data from an xml tree + */ + +struct box * make_box(xmlNode * n, struct css_style * style, struct css_stylesheet * stylesheet, + struct css_selector ** selector, unsigned int depth, + struct box * parent, struct box * prev, struct box * containing_block, + struct box ** inline_parent) +{ + struct box * box = xcalloc(1, sizeof(struct box)); + xmlNode * c; + unsigned int i; + + box->node = n; + box->parent = parent; + + if (n->type == XML_ELEMENT_NODE) { + *selector = xrealloc(*selector, (depth + 1) * sizeof(struct css_selector)); + (*selector)[depth].element = n->name; + (*selector)[depth].class = (*selector)[depth].id = 0; + + box->style = xcalloc(1, sizeof(struct css_style)); + memcpy(box->style, style, sizeof(struct css_style)); + css_get_style(stylesheet, *selector, depth + 1, box->style); + + switch (box->style->display) { + case CSS_DISPLAY_BLOCK: + box->type = BOX_BLOCK; + break; + case CSS_DISPLAY_INLINE: + box->type = BOX_INLINE; + break; + case CSS_DISPLAY_NONE: + default: + free(box->style); + free(box); + return 0; + } + } else if (n->type == XML_TEXT_NODE) { + /* anonymous inline box */ + box->type = BOX_INLINE; + } + + for (i = 0; i < depth; i++) + printf(" "); + printf("make_box: %s: %s\n", box->type == BOX_INLINE ? "inline" : "block", n->name); + + if (*inline_parent && box->type == BOX_BLOCK) { + /* block following inline: end inline_parent */ + printf("ending anonymous container for inlines\n"); + (*inline_parent)->next = box; + *inline_parent = 0; + } else if (*inline_parent && box->type == BOX_INLINE) { + /* inline following inline */ + prev->next = box; + } else if (!(*inline_parent) && box->type == BOX_BLOCK) { + /* block following block */ + if (prev) prev->next = box; + } else if (!(*inline_parent) && box->type == BOX_INLINE) { + /* inline following block: create anonymous container block */ + printf("starting anonymous container for inlines\n"); + *inline_parent = xcalloc(1, sizeof(struct box)); + (*inline_parent)->parent = parent; + if (prev) prev->next = *inline_parent; + (*inline_parent)->children = box; + } + + +/* for (i = 0; i < depth; i++) + printf(" "); + printf("%s ", n->name); + css_dump_style(data->style);*/ + + { + struct box * prev_c; + struct box * b; + struct box * containing; + struct box * inline_parent_c = 0; + + if (box->type == BOX_BLOCK) { + prev_c = 0; + containing = box; + } else { + prev_c = box; + containing = containing_block; + } + + for (c = n->children; c != 0; c = c->next) { + b = make_box(c, box->style, stylesheet, selector, depth + 1, + box, prev_c, containing, &inline_parent_c); + if (!box->children) box->children = b; + if (b) prev_c = b; + } + } + + return box; +} + + +void dump_box(struct box * box, unsigned int depth) +{ + unsigned int i; + struct box * c; + + for (i = 0; i < depth; i++) + printf(" "); + + printf("%s: %s\n", box->type == BOX_INLINE ? "inline" : "block", + box->node->name); + + for (c = box->children; c != 0; c = c->next) + dump_box(c, depth + 1); +} + + +int main(int argc, char *argv[]) +{ +/* struct layout canvas = { 0, 0, 79, 0 }; */ + + struct css_stylesheet * stylesheet; + struct css_style * style = xcalloc(1, sizeof(struct css_style)); + struct css_selector * selector = xcalloc(1, sizeof(struct css_selector)); + xmlNode * c; + xmlDoc * doc; + struct box * box; + struct box * inline_parent = 0; + + doc = xmlParseFile(argv[1]); + if (doc == 0) die("xmlParseFile failed"); + + for (c = doc->children; c != 0 && c->type != XML_ELEMENT_NODE; c = c->next) + ; + if (c == 0) die("no element in document"); + if (strcmp(c->name, "html")) die("document is not html"); + + stylesheet = css_new_stylesheet(); + css_parse_stylesheet(stylesheet, load(argv[2])); + + box = make_box(c, style, stylesheet, &selector, 0, 0, 0, 0, &inline_parent); + dump_box(box, 0); + +/* walk(c, 0); + layout_element(c, 79); + walk(c, 0); + printf("\n\n"); + render_plain(c);*/ + + return 0; +} + + +/******************************************************************************/ + diff --git a/utils/utils.c b/utils/utils.c new file mode 100644 index 000000000..a5df77f7b --- /dev/null +++ b/utils/utils.c @@ -0,0 +1,75 @@ +/** + * $Id: utils.c,v 1.1.1.1 2002/04/22 09:24:34 bursa Exp $ + */ + +#include +#include +#include +#include +#include "utils.h" + +void die(const char * const error) +{ + fprintf(stderr, "Fatal: %s\n", error); + exit(EXIT_FAILURE); +} + +char * strip(char * const s) +{ + size_t i; + for (i = strlen(s); i != 0 && isspace(s[i-1]); i--) + ; + s[i] = 0; + return s + strspn(s, " \t\r\n"); +} + +int whitespace(const char * str) +{ + unsigned int i; + for (i = 0; i < strlen(str); i++) + if (!isspace(str[i])) + return 0; + return 1; +} + +void * xcalloc(const size_t n, const size_t size) +{ + void * p = calloc(n, size); + if (p == 0) die("Out of memory in xcalloc()"); + return p; +} + +void * xrealloc(void * p, const size_t size) +{ + p = realloc(p, size); + if (p == 0) die("Out of memory in xrealloc()"); + return p; +} + +char * xstrdup(const char * const s) +{ + char * c = malloc(strlen(s) + 1); + if (c == 0) die("Out of memory in xstrdup()"); + strcpy(c, s); + return c; +} + +#define CHUNK 0x100 + +char * load(const char * const path) +{ + FILE * fp = fopen(path, "r"); + unsigned int l = 0; + char * buf = malloc(CHUNK); + if (buf == 0) die("Out of memory in load()"); + while (1) { + unsigned int i; + for (i = 0; i != CHUNK && (buf[l] = fgetc(fp)) != EOF; i++, l++) + ; + if (i != CHUNK) break; + buf = xrealloc(buf, l + CHUNK); + } + buf[l] = 0; + fclose(fp); + return buf; +} diff --git a/utils/utils.h b/utils/utils.h new file mode 100644 index 000000000..ced14cb6f --- /dev/null +++ b/utils/utils.h @@ -0,0 +1,11 @@ +/** + * $Id: utils.h,v 1.1.1.1 2002/04/22 09:24:34 bursa Exp $ + */ + +void die(const char * const error); +char * strip(char * const s); +int whitespace(const char * str); +void * xcalloc(const size_t n, const size_t size); +void * xrealloc(void * p, const size_t size); +char * xstrdup(const char * const s); +char * load(const char * const path);