summaryrefslogtreecommitdiff
path: root/render/table.c
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2009-07-23 23:05:34 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2009-07-23 23:05:34 +0000
commitddeadd1c02880367ad786b113d352a519f45ec73 (patch)
tree00b8b46ee1a3fc84e5492c2183dfaa8192d261f9 /render/table.c
parentb20949a35025b23da1bf0ac6003f4575eb94281d (diff)
downloadnetsurf-ddeadd1c02880367ad786b113d352a519f45ec73.tar.gz
netsurf-ddeadd1c02880367ad786b113d352a519f45ec73.tar.bz2
Merge LibCSS port to trunk.
svn path=/trunk/netsurf/; revision=8752
Diffstat (limited to 'render/table.c')
-rw-r--r--render/table.c892
1 files changed, 721 insertions, 171 deletions
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 <assert.h>
#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;
}
+