[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
This commit is contained in:
Richard Wilson 2005-04-14 19:54:24 +00:00
parent 92b2d46874
commit 487cad486d
4 changed files with 424 additions and 57 deletions

View File

@ -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()

View File

@ -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

View File

@ -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,14 +392,32 @@ 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,
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 */
if (style->background_image.type == CSS_BACKGROUND_IMAGE_URI) {
@ -857,6 +877,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.
*

View File

@ -70,6 +70,12 @@ 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);
/**
@ -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) {
@ -2080,6 +2153,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.
*