diff options
author | Richard Wilson <rjw@netsurf-browser.org> | 2005-04-14 19:54:24 +0000 |
---|---|---|
committer | Richard Wilson <rjw@netsurf-browser.org> | 2005-04-14 19:54:24 +0000 |
commit | 487cad486dce950f51c60aee81ba792be1ef2f33 (patch) | |
tree | 1327ba6ce314fb76e4a224ecc8385b326b39e60d | |
parent | 92b2d468740c72094ae47ab1f72995ec60d93ae6 (diff) | |
download | netsurf-487cad486dce950f51c60aee81ba792be1ef2f33.tar.gz netsurf-487cad486dce950f51c60aee81ba792be1ef2f33.tar.bz2 |
[project @ 2005-04-14 19:54:24 by rjw]
Implement HTML table border setting. Improve support for the collapsing border model.
svn path=/import/netsurf/; revision=1632
-rw-r--r-- | css/css.c | 80 | ||||
-rw-r--r-- | css/css.h | 15 | ||||
-rw-r--r-- | render/box_construct.c | 90 | ||||
-rw-r--r-- | render/layout.c | 296 |
4 files changed, 424 insertions, 57 deletions
@@ -2864,6 +2864,86 @@ float css_len2px(const struct css_length *length, } +/** + * Return the most 'eyecatching' border. + * + * \return the most eyecatching border, favoured towards test2 + */ + +struct css_border *css_eyecatching_border(struct css_border *test1, + struct css_style *style1, struct css_border *test2, + struct css_style *style2) +{ + float width1, width2; + int impact = 0; + + assert(test1); + assert(style1); + assert(test2); + assert(style2); + + /* hidden border styles always win, none always loses */ + if ((test1->style == CSS_BORDER_STYLE_HIDDEN) || + (test2->style == CSS_BORDER_STYLE_NONE)) + return test1; + if ((test2->style == CSS_BORDER_STYLE_HIDDEN) || + (test1->style == CSS_BORDER_STYLE_NONE)) + return test2; + + /* the widest border wins */ + width1 = css_len2px(&test1->width.value, style1); + width2 = css_len2px(&test2->width.value, style2); + if (width1 > width2) + return test1; + if (width2 > width1) + return test2; + + /* the closest to a solid line wins */ + switch (test1->style) { + case CSS_BORDER_STYLE_DOUBLE: + impact++; + case CSS_BORDER_STYLE_SOLID: + impact++; + case CSS_BORDER_STYLE_DASHED: + impact++; + case CSS_BORDER_STYLE_DOTTED: + impact++; + case CSS_BORDER_STYLE_RIDGE: + impact++; + case CSS_BORDER_STYLE_OUTSET: + impact++; + case CSS_BORDER_STYLE_GROOVE: + impact++; + case CSS_BORDER_STYLE_INSET: + impact++; + default: + break; + } + switch (test2->style) { + case CSS_BORDER_STYLE_DOUBLE: + impact--; + case CSS_BORDER_STYLE_SOLID: + impact--; + case CSS_BORDER_STYLE_DASHED: + impact--; + case CSS_BORDER_STYLE_DOTTED: + impact--; + case CSS_BORDER_STYLE_RIDGE: + impact--; + case CSS_BORDER_STYLE_OUTSET: + impact--; + case CSS_BORDER_STYLE_GROOVE: + impact--; + case CSS_BORDER_STYLE_INSET: + impact--; + default: + break; + } + if (impact > 0) + return test1; + return test2; +} + #ifdef DEBUG int main() @@ -84,6 +84,12 @@ struct css_border_width { struct css_length value; }; +struct css_border { + colour color; + struct css_border_width width; + css_border_style style; +}; + typedef enum { CSS_CONTENT_STRING, CSS_CONTENT_URI, @@ -163,11 +169,7 @@ struct css_style { css_background_repeat background_repeat; /* borders */ - struct { - colour color; - struct css_border_width width; - css_border_style style; - } border[4]; /**< top, right, bottom, left */ + struct css_border border[4]; /**< top, right, bottom, left */ css_border_collapse border_collapse; struct { enum { CSS_BORDER_SPACING_INHERIT, @@ -630,5 +632,8 @@ void css_dump_stylesheet(const struct css_stylesheet * stylesheet); float css_len2px(const struct css_length *length, const struct css_style *style); +struct css_border *css_eyecatching_border(struct css_border *test1, + struct css_style *style1, struct css_border *test2, + struct css_style *style2); #endif diff --git a/render/box_construct.c b/render/box_construct.c index 3a7c311c9..6b02ac519 100644 --- a/render/box_construct.c +++ b/render/box_construct.c @@ -85,6 +85,7 @@ static struct css_style * box_get_style(struct content *c, xmlNode *n); static void box_solve_display(struct css_style *style, bool root); static void box_set_cellpadding(struct box *box, int value); +static void box_set_table_border(struct box *box, int value, colour color); static void box_text_transform(char *s, unsigned int len, css_text_transform tt); #define BOX_SPECIAL_PARAMS xmlNode *n, struct content *content, \ @@ -263,6 +264,7 @@ bool box_construct_element(xmlNode *n, struct content *content, struct box *inline_container_c; struct css_style *style = 0; struct element_entry *element; + colour border_color; xmlChar *title0; xmlNode *c; @@ -390,13 +392,31 @@ bool box_construct_element(xmlNode *n, struct content *content, box->rows = 1; xmlFree(s); } - if (strcmp((const char *) n->name, "table") == 0 && - (s = (char *) xmlGetProp(n, - (const xmlChar *) "cellpadding"))) { - int value = atoi(s); - if (!strrchr(s, '%') && 0 < value) /* % not implemented */ - box_set_cellpadding(box, value); - xmlFree(s); + if (strcmp((const char *) n->name, "table") == 0) { + border_color = 0x888888; /* default colour */ + if ((s = (char *) xmlGetProp(n, + (const xmlChar *) "cellpadding"))) { + int value = atoi(s); + if (!strrchr(s, '%') && 0 < value) /* % not implemented */ + box_set_cellpadding(box, value); + xmlFree(s); + } + if ((s = (char *) xmlGetProp(n, + (const xmlChar *) "bordercolor"))) { + unsigned int r, g, b; + if (s[0] == '#' && sscanf(s + 1, "%2x%2x%2x", &r, &g, &b) == 3) + border_color = (b << 16) | (g << 8) | r; + else if (s[0] != '#') + border_color = named_colour(s); + xmlFree(s); + } + if ((s = (char *) xmlGetProp(n, + (const xmlChar *) "border"))) { + int value = atoi(s); + if (!strrchr(s, '%') && 0 < value) /* % not implemented */ + box_set_table_border(box, value, border_color); + xmlFree(s); + } } /* fetch any background image for this box */ @@ -858,6 +878,62 @@ void box_set_cellpadding(struct box *box, int value) /** + * Set the borders on a table. + * + * \param box box to set cellpadding on + * \param value border in pixels + * + * The descendants of the box are searched for table cells, and the border is + * set on each one. + */ + +void box_set_table_border(struct box *box, int value, colour color) +{ + struct box *child; + + if (box->type == BOX_TABLE) { + for (unsigned int i = 0; i != 4; i++) { + box->style->border[i].color = color; + box->style->border[i].width.width = + CSS_BORDER_WIDTH_LENGTH; + box->style->border[i].width.value.value = + value; + box->style->border[i].width.value.unit = + CSS_UNIT_PX; + box->style->border[i].style = + CSS_BORDER_STYLE_OUTSET; + } + } + + /* The tree is not normalized yet, so accept cells not in rows and + * rows not in row groups. */ + for (child = box->children; child; child = child->next) { + switch (child->type) { + case BOX_TABLE_ROW_GROUP: + case BOX_TABLE_ROW: + box_set_table_border(child, value, color); + break; + case BOX_TABLE_CELL: + for (unsigned int i = 0; i != 4; i++) { + child->style->border[i].color = color; + child->style->border[i].width.width = + CSS_BORDER_WIDTH_LENGTH; + child->style->border[i].width.value.value = + 1; + child->style->border[i].width.value.unit = + CSS_UNIT_PX; + child->style->border[i].style = + CSS_BORDER_STYLE_INSET; + } + break; + default: + break; + } + } +} + + +/** * Apply the CSS text-transform property to given text for its ASCII chars. * * \param s string to transform diff --git a/render/layout.c b/render/layout.c index a1e470433..5255765c0 100644 --- a/render/layout.c +++ b/render/layout.c @@ -1,7 +1,7 @@ /* * This file is part of NetSurf, http://netsurf.sourceforge.net/ * Licensed under the GNU General Public License, - * http://www.opensource.org/licenses/gpl-license + * http://www.opensource.org/licenses/gpl-license * Copyright 2005 Richard Wilson <info@tinct.net> * Copyright 2005 James Bursa <bursa@users.sourceforge.net> * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> @@ -70,12 +70,18 @@ static void calculate_inline_replaced_widths(struct box *box, int *min, int *max, int *line_max); static void calculate_inline_widths(struct box *box, int *min, int *line_max); static bool calculate_table_widths(struct box *table); +void table_collapse_borders_h(struct box *parent, struct box *child, bool *first); +void table_collapse_borders_v(struct box *row, struct box *cell, unsigned int columns); +void table_collapse_borders_cell(struct box *cell, struct box *right, + struct box *bottom); +void table_remove_borders(struct css_style *style); +struct box *table_find_cell(struct box *table, unsigned int x, unsigned int y); /** * Calculate positions of boxes in a document. * - * \param doc content of type CONTENT_HTML + * \param doc content of type CONTENT_HTML * \param width available page width * \return true on success, false on memory exhaustion */ @@ -510,10 +516,10 @@ void layout_float_find_dimensions(int available_width, * Calculate size of margins, paddings, and borders. * * \param available_width width of containing block - * \param style style giving margins, paddings, and borders - * \param margin[4] filled with margins, may be NULL - * \param padding[4] filled with paddings - * \param border[4] filled with border widths + * \param style style giving margins, paddings, and borders + * \param margin[4] filled with margins, may be NULL + * \param padding[4] filled with paddings + * \param border[4] filled with border widths */ void layout_find_dimensions(int available_width, @@ -565,7 +571,7 @@ void layout_find_dimensions(int available_width, /** * Find y coordinate which clears all floats on left and/or right. * - * \param fl first float in float list + * \param fl first float in float list * \param clear type of clear * \return y coordinate relative to ancestor box for floats */ @@ -590,12 +596,12 @@ int layout_clear(struct box *fl, css_clear clear) /** * Find left and right edges in a vertical range. * - * \param fl first float in float list - * \param y0 start of y range to search - * \param y1 end of y range to search - * \param x0 start left edge, updated to available left edge - * \param x1 start right edge, updated to available right edge - * \param left returns float on left if present + * \param fl first float in float list + * \param y0 start of y range to search + * \param y1 end of y range to search + * \param x0 start left edge, updated to available left edge + * \param x1 start right edge, updated to available right edge + * \param left returns float on left if present * \param right returns float on right if present */ @@ -631,11 +637,11 @@ void find_sides(struct box *fl, int y0, int y1, /** * Layout lines of text or inline boxes with floats. * - * \param box inline container + * \param box inline container * \param width horizontal space available - * \param cont ancestor box which defines horizontal space, for floats - * \param cx box position relative to cont - * \param cy box position relative to cont + * \param cont ancestor box which defines horizontal space, for floats + * \param cx box position relative to cont + * \param cy box position relative to cont * \param content memory pool for any new boxes * \return true on success, false on memory exhaustion */ @@ -706,10 +712,10 @@ int line_height(struct css_style *style) * * \param first box at start of line * \param width available width - * \param y coordinate of top of line, updated on exit to bottom - * \param cx coordinate of left of line relative to cont - * \param cy coordinate of top of line relative to cont - * \param cont ancestor box which defines horizontal space, for floats + * \param y coordinate of top of line, updated on exit to bottom + * \param cx coordinate of left of line relative to cont + * \param cy coordinate of top of line relative to cont + * \param cont ancestor box which defines horizontal space, for floats * \param indent apply any first-line indent * \param next_box updated to first box for next line, or 0 at end * \param content memory pool for any new boxes @@ -932,7 +938,7 @@ bool layout_line(struct box *first, int width, int *y, split_box = b; move_y = true; inline_count++; -/* fprintf(stderr, "layout_line: '%.*s' %li %li\n", b->length, b->text, xp, x); */ +/* fprintf(stderr, "layout_line: '%.*s' %li %li\n", b->length, b->text, xp, x); */ } else if (b->type == BOX_BR) { b->x = x; b->width = 0; @@ -945,7 +951,7 @@ bool layout_line(struct box *first, int width, int *y, /* float */ d = b->children; d->float_children = 0; -/* css_dump_style(b->style); */ +/* css_dump_style(b->style); */ if (!layout_float(d, width, content)) return false; @@ -971,12 +977,12 @@ bool layout_line(struct box *first, int width, int *y, right = b; } b->y = cy; -/* fprintf(stderr, "layout_line: float fits %li %li, edges %li %li\n", */ -/* b->x, b->y, x0, x1); */ +/* fprintf(stderr, "layout_line: float fits %li %li, edges %li %li\n", */ +/* b->x, b->y, x0, x1); */ } else { /* doesn't fit: place below */ place_float_below(b, width, cx, cy + height + 1, cont); -/* fprintf(stderr, "layout_line: float doesn't fit %li %li\n", b->x, b->y); */ +/* fprintf(stderr, "layout_line: float doesn't fit %li %li\n", b->x, b->y); */ } assert(cont->float_children != b); b->next_float = cont->float_children; @@ -1052,12 +1058,12 @@ bool layout_line(struct box *first, int width, int *y, b = c2; } x += space_before + w; -/* fprintf(stderr, "layout_line: overflow, forcing\n"); */ +/* fprintf(stderr, "layout_line: overflow, forcing\n"); */ } else if (space == 0 || x1 - x0 <= x + space_before + w) { /* first word doesn't fit, but full width not available so leave for later */ b = split_box; -/* fprintf(stderr, "layout_line: overflow, leaving\n"); */ +/* fprintf(stderr, "layout_line: overflow, leaving\n"); */ } else { /* fit as many words as possible */ assert(space != 0); @@ -1067,7 +1073,7 @@ bool layout_line(struct box *first, int width, int *y, x1 - x0 - x - space_before, &space, &w); LOG(("'%.*s' %i %u %i", (int) split_box->length, split_box->text, x1 - x0, space, w)); -/* assert(space == split_box->length || split_box->text[space] = ' '); */ +/* assert(space == split_box->length || split_box->text[space] = ' '); */ if (space == 0) space = 1; if (space != split_box->length) { @@ -1096,7 +1102,7 @@ bool layout_line(struct box *first, int width, int *y, b = c2; } x += space_before + w; -/* fprintf(stderr, "layout_line: overflow, fit\n"); */ +/* fprintf(stderr, "layout_line: overflow, fit\n"); */ } move_y = true; } @@ -1151,7 +1157,7 @@ int layout_text_indent(struct css_style *style, int width) /** * Layout the contents of a float or inline block. * - * \param b float or inline block box + * \param b float or inline block box * \param width available width * \param content memory pool for any new boxes * \return true on success, false on memory exhaustion @@ -1176,11 +1182,11 @@ bool layout_float(struct box *b, int width, struct content *content) /** * Position a float in the first available space. * - * \param c float box to position + * \param c float box to position * \param width available width - * \param cx x coordinate relative to cont to place float right of - * \param y y coordinate relative to cont to place float below - * \param cont ancestor box which defines horizontal space, for floats + * \param cx x coordinate relative to cont to place float right of + * \param y y coordinate relative to cont to place float below + * \param cont ancestor box which defines horizontal space, for floats */ void place_float_below(struct box *c, int width, int cx, int y, @@ -1216,9 +1222,9 @@ void place_float_below(struct box *c, int width, int cx, int y, /** * Layout a table. * - * \param table table to layout + * \param table table to layout * \param available_width width of containing block - * \param content memory pool for any new boxes + * \param content memory pool for any new boxes * \return true on success, false on memory exhaustion */ @@ -1321,7 +1327,7 @@ bool layout_table(struct box *table, int available_width, table->padding[RIGHT] + table->border[RIGHT] + (table->margin[RIGHT] == AUTO ? 0 : - table->margin[RIGHT])); + table->margin[RIGHT])); break; } @@ -1579,8 +1585,8 @@ bool layout_table(struct box *table, int available_width, for (c = row->children; c; c = c->next) { /* unextended bottom padding is in c->descendant_y1, and unextended * cell height is in c->descendant_y0 */ - spare_height = (c->padding[BOTTOM] - c->descendant_y1) + - (c->height - c->descendant_y0); + spare_height = (c->padding[BOTTOM] - c->descendant_y1) + + (c->height - c->descendant_y0); switch (c->style->vertical_align.type) { case CSS_VERTICAL_ALIGN_SUB: case CSS_VERTICAL_ALIGN_SUPER: @@ -1628,8 +1634,8 @@ bool layout_table(struct box *table, int available_width, * Moves the children of a box by a specified amount * * \param box top of tree of boxes - * \param x the amount to move children by horizontally - * \param y the amount to move children by vertically + * \param x the amount to move children by horizontally + * \param y the amount to move children by vertically */ void layout_move_children(struct box *box, int x, int y) { @@ -1729,9 +1735,9 @@ bool calculate_widths(struct box *box) * Find min, max widths for a BOX_BLOCK, BOX_INLINE_BLOCK, BOX_FLOAT_*, * or BOX_TABLE. * - * \param box BLOCK, INLINE_BLOCK, FLOAT, or TABLE box - * \param min current min, updated to new min - * \param max current max, updated to new max + * \param box BLOCK, INLINE_BLOCK, FLOAT, or TABLE box + * \param min current min, updated to new min + * \param max current max, updated to new max * \param max_sum sum of maximum widths, updated, or 0 if not required * \return true on success, false on memory exhaustion */ @@ -1918,6 +1924,7 @@ bool calculate_table_widths(struct box *table) struct box *row_group, *row, *cell; int width, min_width = 0, max_width = 0; struct column *col; + bool first; LOG(("table %p, columns %u", table, table->columns)); @@ -1937,6 +1944,72 @@ bool calculate_table_widths(struct box *table) assert(table->children && table->children->children); + /* handle collapsing border model */ + assert(table->style); + if (table->style->border_collapse == CSS_BORDER_COLLAPSE_COLLAPSE) { + + /* 1st stage: collapse all borders down to the cells */ + first = true; + for (row_group = table->children; row_group; + row_group = row_group->next) { + assert(row_group->type == BOX_TABLE_ROW_GROUP); + assert(row_group->style); + table_collapse_borders_h(table, row_group, &first); + first = (row_group->children); + for (row = row_group->children; row; row = row->next) { + assert(row->type == BOX_TABLE_ROW); + assert(row->style); + table_collapse_borders_h(row_group, row, &first); + for (cell = row->children; cell; cell = cell->next) { + assert(cell->type == BOX_TABLE_CELL); + assert(cell->style); + table_collapse_borders_v(row, cell, table->columns); + } + table_remove_borders(row->style); + } + table_remove_borders(row_group->style); + } + table_remove_borders(table->style); + + /* 2nd stage: rather than building a grid of cells, we slowly look up the + * cell we want to collapse with */ + for (i = 0; i < table->columns; i++) { + for (j = 0; j < table->rows; j++) { + table_collapse_borders_cell( + table_find_cell(table, i, j), + table_find_cell(table, i + 1, j), + table_find_cell(table, i, j + 1)); + } + } + + /* 3rd stage: remove redundant borders */ + first = true; + for (row_group = table->children; row_group; + row_group = row_group->next) { + for (row = row_group->children; row; row = row->next) { + for (cell = row->children; cell; cell = cell->next) { + if (!first) { + cell->style->border[TOP].style = + CSS_BORDER_STYLE_NONE; + cell->style->border[TOP].width.value.value = + 0; + cell->style->border[TOP].width.value.unit = + CSS_UNIT_PX; + } + if (cell->start_column > 0) { + cell->style->border[LEFT].style = + CSS_BORDER_STYLE_NONE; + cell->style->border[LEFT].width.value.value = + 0; + cell->style->border[LEFT].width.value.unit = + CSS_UNIT_PX; + } + } + first = false; + } + } + } + /* 1st pass: consider cells with colspan 1 only */ for (row_group = table->children; row_group; row_group = row_group->next) { @@ -2081,6 +2154,139 @@ bool calculate_table_widths(struct box *table) /** + * Collapse the borders of two boxes together. + */ + +void table_collapse_borders_v(struct box *row, struct box *cell, unsigned int columns) +{ + struct css_border *border; + + if (cell->start_column == 0) { + border = css_eyecatching_border(&row->style->border[LEFT], row->style, + &cell->style->border[LEFT], cell->style); + cell->style->border[LEFT] = *border; + } + border = css_eyecatching_border(&row->style->border[TOP], row->style, + &cell->style->border[TOP], cell->style); + cell->style->border[TOP] = *border; + border = css_eyecatching_border(&row->style->border[BOTTOM], row->style, + &cell->style->border[BOTTOM], cell->style); + cell->style->border[BOTTOM] = *border; + if ((cell->start_column + cell->columns) == columns) { + border = css_eyecatching_border(&row->style->border[RIGHT], row->style, + &cell->style->border[RIGHT], cell->style); + cell->style->border[RIGHT] = *border; + } +} + + +/** + * Collapse the borders of two boxes together. + */ + +void table_collapse_borders_h(struct box *parent, struct box *child, bool *first) +{ + struct css_border *border; + + if (*first) { + border = css_eyecatching_border(&parent->style->border[TOP], parent->style, + &child->style->border[TOP], child->style); + child->style->border[TOP] = *border; + *first = false; + } + border = css_eyecatching_border(&parent->style->border[LEFT], parent->style, + &child->style->border[LEFT], child->style); + child->style->border[LEFT] = *border; + border = css_eyecatching_border(&parent->style->border[RIGHT], parent->style, + &child->style->border[RIGHT], child->style); + child->style->border[RIGHT] = *border; + if (!child->next) { + border = css_eyecatching_border(&parent->style->border[BOTTOM], parent->style, + &child->style->border[BOTTOM], child->style); + child->style->border[BOTTOM] = *border; + } +} + + +/** + * Collapse the borders of two boxes together. + */ + +void table_collapse_borders_cell(struct box *cell, struct box *right, + struct box *bottom) { + struct css_border *border; + + if (!cell) + return; + + if ((right) && (right != cell)) { + border = css_eyecatching_border(&cell->style->border[RIGHT], cell->style, + &right->style->border[LEFT], right->style); + cell->style->border[RIGHT] = *border; + + } + if ((bottom) && (bottom != cell)) { + border = css_eyecatching_border(&cell->style->border[BOTTOM], cell->style, + &bottom->style->border[TOP], bottom->style); + cell->style->border[BOTTOM] = *border; + } +} + + +/** + * Removes all borders. + */ + +void table_remove_borders(struct css_style *style) +{ + int i; + + for (i = 0; i < 4; i++) { + style->border[i].style = CSS_BORDER_STYLE_NONE; + style->border[i].width.value.value = 0; + style->border[i].width.value.unit = CSS_UNIT_PX; + } +} + + +/** + * Find a cell occupying a particular position in a table grid + */ + +struct box *table_find_cell(struct box *table, unsigned int x, + unsigned int y) +{ + struct box *row_group, *row, *cell; + struct box *match = NULL; + unsigned int cur_row = 0; + + if ((x > table->columns) || (y > table->rows)) + return NULL; + + /* this code uses brute-force and should be re-implemented using a faster + * algorithm */ + for (row_group = table->children; row_group; + row_group = row_group->next) { + for (row = row_group->children; row; row = row->next) { + for (cell = row->children; cell; cell = cell->next) { + if (cell->start_column > x) + break; + if ((cell->start_column <= x) && + (x < (cell->start_column + + cell->columns))) + match = cell; + } + if (cur_row == y) + return match; + if (++cur_row > y) + return NULL; + } + } + + return NULL; +} + +/** * Recursively calculate the descendant_[xy][01] values for a laid-out box tree. * * \param box tree of boxes to update |