From ddeadd1c02880367ad786b113d352a519f45ec73 Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Thu, 23 Jul 2009 23:05:34 +0000 Subject: Merge LibCSS port to trunk. svn path=/trunk/netsurf/; revision=8752 --- render/table.c | 892 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 721 insertions(+), 171 deletions(-) (limited to 'render/table.c') diff --git a/render/table.c b/render/table.c index d3134f687..aad3e38c2 100644 --- a/render/table.c +++ b/render/table.c @@ -23,6 +23,7 @@ #include #include "css/css.h" +#include "css/utils.h" #include "render/box.h" #include "render/table.h" #define NDEBUG @@ -30,15 +31,29 @@ #undef NDEBUG #include "utils/talloc.h" - -static void table_collapse_borders_h(struct box *parent, struct box *child, - bool *first); -static void table_collapse_borders_v(struct box *row, struct box *cell, - unsigned int columns); -static void table_collapse_borders_cell(struct box *cell, struct box *right, - struct box *bottom); -static void table_remove_borders(struct css_style *style); -struct box *table_find_cell(struct box *table, unsigned int x, unsigned int y); +/** + * Container for border values during table border calculations + */ +struct border { + enum css_border_style style; /**< border-style */ + enum css_border_color color; /**< border-color type */ + css_color c; /**< border-color value */ + css_fixed width; /**< border-width length */ + css_unit unit; /**< border-width units */ +}; + +static void table_used_left_border_for_cell(struct box *cell); +static void table_used_top_border_for_cell(struct box *cell); +static void table_used_right_border_for_cell(struct box *cell); +static void table_used_bottom_border_for_cell(struct box *cell); +static bool table_border_is_more_eyecatching(const struct border *a, + box_type a_src, const struct border *b, box_type b_src); +static void table_cell_top_process_table(struct box *table, struct border *a, + box_type *a_src); +static bool table_cell_top_process_group(struct box *cell, struct box *group, + struct border *a, box_type *a_src); +static bool table_cell_top_process_row(struct box *cell, struct box *row, + struct border *a, box_type *a_src); /** @@ -75,6 +90,10 @@ bool table_calculate_column_types(struct box *table) 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) { + enum css_width type; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + assert(cell->type == BOX_TABLE_CELL); assert(cell->style); @@ -82,17 +101,21 @@ bool table_calculate_column_types(struct box *table) continue; i = cell->start_column; - if (cell->style->position != CSS_POSITION_ABSOLUTE && - cell->style->position != CSS_POSITION_FIXED) { + if (css_computed_position(cell->style) != + CSS_POSITION_ABSOLUTE && + css_computed_position(cell->style) != + CSS_POSITION_FIXED) { col[i].positioned = false; } + type = css_computed_width(cell->style, &value, &unit); + /* fixed width takes priority over any other width type */ if (col[i].type != COLUMN_WIDTH_FIXED && - cell->style->width.width == CSS_WIDTH_LENGTH) { + type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT) { col[i].type = COLUMN_WIDTH_FIXED; - col[i].width = css_len2px(&cell->style-> - width.value.length, cell->style); + col[i].width = FIXTOINT(nscss_len2px(value, unit, + cell->style)); if (col[i].width < 0) col[i].width = 0; continue; @@ -101,12 +124,12 @@ bool table_calculate_column_types(struct box *table) if (col[i].type != COLUMN_WIDTH_UNKNOWN) continue; - if (cell->style->width.width == CSS_WIDTH_PERCENT) { + if (type == CSS_WIDTH_SET && unit == CSS_UNIT_PCT) { col[i].type = COLUMN_WIDTH_PERCENT; - col[i].width = cell->style->width.value.percent; + col[i].width = FIXTOINT(value); if (col[i].width < 0) col[i].width = 0; - } else if (cell->style->width.width == CSS_WIDTH_AUTO) { + } else if (type == CSS_WIDTH_AUTO) { col[i].type = COLUMN_WIDTH_AUTO; } } @@ -118,6 +141,9 @@ bool table_calculate_column_types(struct box *table) unsigned int fixed_columns = 0, percent_columns = 0, auto_columns = 0, unknown_columns = 0; int fixed_width = 0, percent_width = 0; + enum css_width type; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; if (cell->columns == 1) continue; @@ -145,14 +171,16 @@ bool table_calculate_column_types(struct box *table) if (!unknown_columns) continue; + type = css_computed_width(cell->style, &value, &unit); + /* if cell is fixed width, and all spanned columns are fixed * or unknown width, split extra width among unknown columns */ - if (cell->style->width.width == CSS_WIDTH_LENGTH && + if (type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT && fixed_columns + unknown_columns == cell->columns) { - int width = (css_len2px(&cell->style-> - width.value.length, cell->style) - - fixed_width) / unknown_columns; + int width = (FIXTOFLT(nscss_len2px(value, unit, + cell->style)) - fixed_width) / + unknown_columns; if (width < 0) width = 0; for (j = 0; j != cell->columns; j++) { @@ -164,10 +192,10 @@ bool table_calculate_column_types(struct box *table) } /* as above for percentage width */ - if (cell->style->width.width == CSS_WIDTH_PERCENT && + if (type == CSS_WIDTH_SET && unit == CSS_UNIT_PCT && percent_columns + unknown_columns == cell->columns) { - int width = (cell->style->width.value.percent - + int width = (FIXTOFLT(value) - percent_width) / unknown_columns; if (width < 0) width = 0; @@ -195,212 +223,734 @@ bool table_calculate_column_types(struct box *table) return true; } - /** - * Handle collapsing border model. + * Calculate used values of border-{trbl}-{style,color,width} for table cells. * - * \param table box of type BOX_TABLE + * \param cell Table cell to consider + * + * \post \a cell's border array is populated */ +void table_used_border_for_cell(struct box *cell) +{ + int side; + + assert(cell->type == BOX_TABLE_CELL); + + if (css_computed_border_collapse(cell->style) == + CSS_BORDER_COLLAPSE_SEPARATE) { + css_fixed width = 0; + css_unit unit = CSS_UNIT_PX; + + /* Left border */ + cell->border[LEFT].style = + css_computed_border_left_style(cell->style); + cell->border[LEFT].color = + css_computed_border_left_color(cell->style, + &cell->border[LEFT].c); + css_computed_border_left_width(cell->style, &width, &unit); + cell->border[LEFT].width = + FIXTOINT(nscss_len2px(width, unit, cell->style)); + + /* Top border */ + cell->border[TOP].style = + css_computed_border_top_style(cell->style); + cell->border[TOP].color = + css_computed_border_top_color(cell->style, + &cell->border[TOP].c); + css_computed_border_top_width(cell->style, &width, &unit); + cell->border[TOP].width = + FIXTOINT(nscss_len2px(width, unit, cell->style)); + + /* Right border */ + cell->border[RIGHT].style = + css_computed_border_right_style(cell->style); + cell->border[RIGHT].color = + css_computed_border_right_color(cell->style, + &cell->border[RIGHT].c); + css_computed_border_right_width(cell->style, &width, &unit); + cell->border[RIGHT].width = + FIXTOINT(nscss_len2px(width, unit, cell->style)); + + /* Bottom border */ + cell->border[BOTTOM].style = + css_computed_border_bottom_style(cell->style); + cell->border[BOTTOM].color = + css_computed_border_bottom_color(cell->style, + &cell->border[BOTTOM].c); + css_computed_border_bottom_width(cell->style, &width, &unit); + cell->border[BOTTOM].width = + FIXTOINT(nscss_len2px(width, unit, cell->style)); + } else { + /* Left border */ + table_used_left_border_for_cell(cell); + + /* Top border */ + table_used_top_border_for_cell(cell); + + /* Right border */ + table_used_right_border_for_cell(cell); + + /* Bottom border */ + table_used_bottom_border_for_cell(cell); + } + + /* Finally, ensure that any borders configured as + * hidden or none have zero width. (c.f. layout_find_dimensions) */ + for (side = 0; side != 4; side++) { + if (cell->border[side].style == CSS_BORDER_STYLE_HIDDEN || + cell->border[side].style == + CSS_BORDER_STYLE_NONE) + cell->border[side].width = 0; + } +} + +/****************************************************************************** + * Helpers for used border calculations * + ******************************************************************************/ -void table_collapse_borders(struct box *table) +/** + * Calculate used values of border-left-{style,color,width} + * + * \param cell Table cell to consider + */ +void table_used_left_border_for_cell(struct box *cell) { - bool first; - unsigned int i, j; - struct box *row_group, *row, *cell; + struct border a, b; + box_type a_src, b_src; + + /** \todo Need column and column_group, too */ + + /* Initialise to computed left border for cell */ + a.style = css_computed_border_left_style(cell->style); + a.color = css_computed_border_left_color(cell->style, &a.c); + css_computed_border_left_width(cell->style, &a.width, &a.unit); + a_src = BOX_TABLE_CELL; + + if (cell->prev != NULL || cell->start_column != 0) { + /* Cell to the left -- consider its right border */ + struct box *prev = NULL; + + if (cell->prev == NULL) { + struct box *row; + + /* Spanned from a previous row */ + for (row = cell->parent; row != NULL; row = row->prev) { + for (prev = row->children; prev != NULL; + prev = prev->next) { + if (prev->start_column + + prev->columns == + cell->start_column) + break; + } - assert(table->type == BOX_TABLE); - - /* 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 != NULL; - 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); + if (prev != NULL) + break; } - table_remove_borders(row->style); + + assert(prev != NULL); + } else { + prev = cell->prev; + } + + b.style = css_computed_border_right_style(prev->style); + b.color = css_computed_border_right_color(prev->style, &b.c); + css_computed_border_right_width(prev->style, &b.width, &b.unit); + b_src = BOX_TABLE_CELL; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } + } else { + /* First cell in row, so consider rows and row group */ + struct box *row = cell->parent; + struct box *group = row->parent; + struct box *table = group->parent; + unsigned int rows = cell->rows; + + while (rows-- > 0 && row != NULL) { + /* Spanned rows -- consider their left border */ + b.style = css_computed_border_left_style(row->style); + b.color = css_computed_border_left_color( + row->style, &b.c); + css_computed_border_left_width( + row->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW; + + if (table_border_is_more_eyecatching(&a, a_src, + &b, b_src)) { + a = b; + a_src = b_src; + } + + row = row->next; + } + + /** \todo can cells span row groups? */ + + /* Row group -- consider its left border */ + b.style = css_computed_border_left_style(group->style); + b.color = css_computed_border_left_color(group->style, &b.c); + css_computed_border_left_width(group->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW_GROUP; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } + + /* The table itself -- consider its left border */ + b.style = css_computed_border_left_style(table->style); + b.color = css_computed_border_left_color(table->style, &b.c); + css_computed_border_left_width(table->style, &b.width, &b.unit); + b_src = BOX_TABLE; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; } - 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)); + + /* a now contains the used left border for the cell */ + cell->border[LEFT].style = a.style; + cell->border[LEFT].color = a.color; + cell->border[LEFT].c = a.c; + cell->border[LEFT].width = + FIXTOINT(nscss_len2px(a.width, a.unit, cell->style)); +} + +/** + * Calculate used values of border-top-{style,color,width} + * + * \param cell Table cell to consider + */ +void table_used_top_border_for_cell(struct box *cell) +{ + struct border a, b; + box_type a_src, b_src; + struct box *row = cell->parent; + bool process_group = false; + + /* Initialise to computed top border for cell */ + a.style = css_computed_border_top_style(cell->style); + a.color = css_computed_border_top_color(cell->style, &a.c); + css_computed_border_top_width(cell->style, &a.width, &a.unit); + a_src = BOX_TABLE_CELL; + + /* Top border of row */ + b.style = css_computed_border_top_style(row->style); + b.color = css_computed_border_top_color(row->style, &b.c); + css_computed_border_top_width(row->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } + + if (row->prev != NULL) { + /* Consider row(s) above */ + while (table_cell_top_process_row(cell, row->prev, + &a, &a_src) == false) { + if (row->prev->prev == NULL) { + /* Consider row group */ + process_group = true; + break; + } else { + row = row->prev; + } } + } else { + process_group = true; } - /* 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; + if (process_group) { + struct box *group = row->parent; + + /* Top border of row group */ + b.style = css_computed_border_top_style(group->style); + b.color = css_computed_border_top_color(group->style, &b.c); + css_computed_border_top_width(group->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW_GROUP; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } + + if (group->prev == NULL) { + /* Top border of table */ + table_cell_top_process_table(group->parent, &a, &a_src); + } else { + /* Process previous group(s) */ + while (table_cell_top_process_group(cell, group->prev, + &a, &a_src) == false) { + if (group->prev->prev == NULL) { + /* Top border of table */ + table_cell_top_process_table( + group->parent, + &a, &a_src); + break; + } else { + group = group->prev; } } - first = false; } } -} + /* a now contains the used top border for the cell */ + cell->border[TOP].style = a.style; + cell->border[TOP].color = a.color; + cell->border[TOP].c = a.c; + cell->border[TOP].width = + FIXTOINT(nscss_len2px(a.width, a.unit, cell->style)); +} /** - * Collapse the borders of two boxes together. + * Calculate used values of border-right-{style,color,width} + * + * \param cell Table cell to consider */ - -void table_collapse_borders_v(struct box *row, struct box *cell, unsigned int columns) +void table_used_right_border_for_cell(struct box *cell) { - struct css_border *border; + struct border a, b; + box_type a_src, b_src; + + /** \todo Need column and column_group, too */ + + /* Initialise to computed right border for cell */ + a.style = css_computed_border_right_style(cell->style); + a.color = css_computed_border_right_color(cell->style, &a.c); + css_computed_border_right_width(cell->style, &a.width, &a.unit); + a_src = BOX_TABLE_CELL; + + if (cell->next != NULL || cell->start_column + cell->columns != + cell->parent->parent->parent->columns) { + /* Cell is not at right edge of table -- no right border */ + a.style = CSS_BORDER_STYLE_NONE; + a.width = 0; + a.unit = CSS_UNIT_PX; + } else { + /* Last cell in row, so consider rows and row group */ + struct box *row = cell->parent; + struct box *group = row->parent; + struct box *table = group->parent; + unsigned int rows = cell->rows; + + while (rows-- > 0 && row != NULL) { + /* Spanned rows -- consider their right border */ + b.style = css_computed_border_right_style(row->style); + b.color = css_computed_border_right_color( + row->style, &b.c); + css_computed_border_right_width( + row->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW; + + if (table_border_is_more_eyecatching(&a, a_src, + &b, b_src)) { + a = b; + a_src = b_src; + } - 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; + row = row->next; + } + + /** \todo can cells span row groups? */ + + /* Row group -- consider its right border */ + b.style = css_computed_border_right_style(group->style); + b.color = css_computed_border_right_color(group->style, &b.c); + css_computed_border_right_width(group->style, + &b.width, &b.unit); + b_src = BOX_TABLE_ROW_GROUP; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } + + /* The table itself -- consider its right border */ + b.style = css_computed_border_right_style(table->style); + b.color = css_computed_border_right_color(table->style, &b.c); + css_computed_border_right_width(table->style, + &b.width, &b.unit); + b_src = BOX_TABLE; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } } -} + /* a now contains the used right border for the cell */ + cell->border[RIGHT].style = a.style; + cell->border[RIGHT].color = a.color; + cell->border[RIGHT].c = a.c; + cell->border[RIGHT].width = + FIXTOINT(nscss_len2px(a.width, a.unit, cell->style)); +} /** - * Collapse the borders of two boxes together. + * Calculate used values of border-bottom-{style,color,width} + * + * \param cell Table cell to consider */ - -void table_collapse_borders_h(struct box *parent, struct box *child, bool *first) +void table_used_bottom_border_for_cell(struct box *cell) { - struct css_border *border; + struct border a, b; + box_type a_src, b_src; + struct box *row = cell->parent; + unsigned int rows = cell->rows; + + /* Initialise to computed bottom border for cell */ + a.style = css_computed_border_bottom_style(cell->style); + a.color = css_computed_border_bottom_color(cell->style, &a.c); + css_computed_border_bottom_width(cell->style, &a.width, &a.unit); + a_src = BOX_TABLE_CELL; + + while (rows-- > 0 && row != NULL) + row = row->next; + + /** \todo Can cells span row groups? */ + + if (row != NULL) { + /* Cell is not at bottom edge of table -- no bottom border */ + a.style = CSS_BORDER_STYLE_NONE; + a.width = 0; + a.unit = CSS_UNIT_PX; + } else { + /* Cell at bottom of table, so consider row and row group */ + struct box *row = cell->parent; + struct box *group = row->parent; + struct box *table = group->parent; + + /* Bottom border of row */ + b.style = css_computed_border_bottom_style(row->style); + b.color = css_computed_border_bottom_color(row->style, &b.c); + css_computed_border_bottom_width(row->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } - 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; + /* Row group -- consider its bottom border */ + b.style = css_computed_border_bottom_style(group->style); + b.color = css_computed_border_bottom_color(group->style, &b.c); + css_computed_border_bottom_width(group->style, + &b.width, &b.unit); + b_src = BOX_TABLE_ROW_GROUP; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } + + /* The table itself -- consider its bottom border */ + b.style = css_computed_border_bottom_style(table->style); + b.color = css_computed_border_bottom_color(table->style, &b.c); + css_computed_border_bottom_width(table->style, + &b.width, &b.unit); + b_src = BOX_TABLE; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } } -} + /* a now contains the used bottom border for the cell */ + cell->border[BOTTOM].style = a.style; + cell->border[BOTTOM].color = a.color; + cell->border[BOTTOM].c = a.c; + cell->border[BOTTOM].width = + FIXTOINT(nscss_len2px(a.width, a.unit, cell->style)); +} /** - * Collapse the borders of two boxes together. + * Determine if a border style is more eyecatching than another + * + * \param a Reference border style + * \param a_src Source of \a a + * \param b Candidate border style + * \param b_src Source of \a b + * \return True if \a b is more eyecatching than \a a */ +bool table_border_is_more_eyecatching(const struct border *a, + box_type a_src, const struct border *b, box_type b_src) +{ + css_fixed awidth, bwidth; + int impact = 0; -void table_collapse_borders_cell(struct box *cell, struct box *right, - struct box *bottom) { - struct css_border *border; + /* See CSS 2.1 $17.6.2.1 */ - if (!cell) - return; + /* 1 + 2 -- hidden beats everything, none beats nothing */ + if (a->style == CSS_BORDER_STYLE_HIDDEN || + b->style == CSS_BORDER_STYLE_NONE) + return false; + + if (b->style == CSS_BORDER_STYLE_HIDDEN || + a->style == CSS_BORDER_STYLE_NONE) + return true; + + /* 3a -- wider borders beat narrow ones */ + /* The widths must be absolute, which will be the case + * if they've come from a computed style. */ + assert(a->unit != CSS_UNIT_EM && a->unit != CSS_UNIT_EX); + assert(b->unit != CSS_UNIT_EM && b->unit != CSS_UNIT_EX); + awidth = nscss_len2px(a->width, a->unit, NULL); + bwidth = nscss_len2px(b->width, b->unit, NULL); - 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 (awidth < bwidth) + return true; + else if (bwidth < awidth) + return false; + /* 3b -- sort by style */ + switch (a->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 ((bottom) && (bottom != cell)) { - border = css_eyecatching_border(&cell->style->border[BOTTOM], cell->style, - &bottom->style->border[TOP], bottom->style); - cell->style->border[BOTTOM] = *border; + + switch (b->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 true; + else if (impact > 0) + return false; + + /* 4a -- sort by origin */ + impact = 0; + + switch (a_src) { + case BOX_TABLE_CELL: impact++; + case BOX_TABLE_ROW: impact++; + case BOX_TABLE_ROW_GROUP: impact++; + /** \todo COL/COL_GROUP */ + case BOX_TABLE: impact++; + default: + break; + } + + switch (b_src) { + case BOX_TABLE_CELL: impact--; + case BOX_TABLE_ROW: impact--; + case BOX_TABLE_ROW_GROUP: impact--; + /** \todo COL/COL_GROUP */ + case BOX_TABLE: impact--; + default: + break; + } + + if (impact < 0) + return true; + else if (impact > 0) + return false; + + /* 4b -- furthest left (if direction: ltr) and towards top wins */ + /** \todo Currently assumes b satisifies this */ + return true; } +/****************************************************************************** + * Helpers for top border collapsing * + ******************************************************************************/ /** - * Removes all borders. + * Process a table + * + * \param table Table to process + * \param a Current border style for cell + * \param a_src Source of \a a + * + * \post \a a will be updated with most eyecatching style + * \post \a a_src will be updated also */ +void table_cell_top_process_table(struct box *table, struct border *a, + box_type *a_src) +{ + struct border b; + box_type b_src; + + /* Top border of table */ + b.style = css_computed_border_top_style(table->style); + b.color = css_computed_border_top_color(table->style, &b.c); + css_computed_border_top_width(table->style, &b.width, &b.unit); + b_src = BOX_TABLE; + + if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; + } +} -void table_remove_borders(struct css_style *style) +/** + * Process a group + * + * \param cell Cell being considered + * \param group Group to process + * \param a Current border style for cell + * \param a_src Source of \a a + * \return true if group has non-empty rows, false otherwise + * + * \post \a a will be updated with most eyecatching style + * \post \a a_src will be updated also + */ +bool table_cell_top_process_group(struct box *cell, struct box *group, + struct border *a, box_type *a_src) { - int i; + struct border b; + box_type b_src; + + /* Bottom border of group */ + b.style = css_computed_border_bottom_style(group->style); + b.color = css_computed_border_bottom_color(group->style, &b.c); + css_computed_border_bottom_width(group->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW_GROUP; + + if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; + } - 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; + if (group->last != NULL) { + /* Process rows in group, starting with last */ + struct box *row = group->last; + + while (table_cell_top_process_row(cell, row, + a, a_src) == false) { + if (row->prev == NULL) { + return false; + } else { + row = row->prev; + } + } + } else { + /* Group is empty, so consider its top border */ + b.style = css_computed_border_top_style(group->style); + b.color = css_computed_border_top_color(group->style, &b.c); + css_computed_border_top_width(group->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW_GROUP; + + if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; + } + + return false; } -} + return true; +} /** - * Find a cell occupying a particular position in a table grid. + * Process a row + * + * \param cell Cell being considered + * \param row Row to process + * \param a Current border style for cell + * \param a_src Source of \a a + * \return true if row has cells, false otherwise + * + * \post \a a will be updated with most eyecatching style + * \post \a a_src will be updated also */ - -struct box *table_find_cell(struct box *table, unsigned int x, - unsigned int y) +bool table_cell_top_process_row(struct box *cell, struct box *row, + struct border *a, box_type *a_src) { - struct box *row_group, *row, *cell; - unsigned int row_num = 0; - - if (table->columns <= x || table->rows <= y) - return 0; + struct border b; + box_type b_src; + + /* Bottom border of row */ + b.style = css_computed_border_bottom_style(row->style); + b.color = css_computed_border_bottom_color(row->style, &b.c); + css_computed_border_bottom_width(row->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW; + + if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; + } - row_group = table->children; - row = row_group->children; + if (row->children == NULL) { + /* Row is empty, so consider its top border */ + b.style = css_computed_border_top_style(row->style); + b.color = css_computed_border_top_color(row->style, &b.c); + css_computed_border_top_width(row->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW; - while (row_num != y) { - if (row->next) { - row = row->next; - } else { - row_group = row_group->next; - row = row_group->children; + if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; } - row_num++; - } + return false; + } else { + /* Process cells that are directly above the cell being + * considered. They may not be in this row, but in one of the + * rows above it in the case where rowspan > 1. */ + struct box *c; + bool processed = false; + + while (processed == false) { + for (c = row->children; c != NULL; c = c->next) { + /* Ignore cells to the left */ + if (c->start_column + c->columns < + cell->start_column) + continue; + /* Ignore cells to the right */ + if (c->start_column >= cell->start_column + + cell->columns) + continue; + + /* Flag that we've processed a cell */ + processed = true; + + /* Consider bottom border */ + b.style = css_computed_border_bottom_style( + c->style); + b.color = css_computed_border_bottom_color( + c->style, &b.c); + css_computed_border_bottom_width(c->style, + &b.width, &b.unit); + b_src = BOX_TABLE_CELL; + + if (table_border_is_more_eyecatching(a, *a_src, + &b, b_src)) { + *a = b; + *a_src = b_src; + } + } - for (cell = row->children; cell; cell = cell->next) - if (cell->start_column <= x && - x < cell->start_column + cell->columns) - break; + if (processed == false) { + /* There must be a preceding row */ + assert(row->prev != NULL); + + row = row->prev; + } + } + } - return cell; + return true; } + -- cgit v1.2.3