Subversion Repositories Kolibri OS

Rev

Rev 3584 | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
  3.  * Copyright 2005 Richard Wilson <info@tinct.net>
  4.  *
  5.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  6.  *
  7.  * NetSurf is free software; you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License as published by
  9.  * the Free Software Foundation; version 2 of the License.
  10.  *
  11.  * NetSurf is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  */
  19.  
  20. /** \file
  21.  * Table processing and layout (implementation).
  22.  */
  23.  
  24. #include <assert.h>
  25.  
  26. #include <dom/dom.h>
  27.  
  28. #include "css/css.h"
  29. #include "css/utils.h"
  30. #include "render/box.h"
  31. #include "render/table.h"
  32. #include "utils/log.h"
  33. #include "utils/talloc.h"
  34.  
  35. /* Define to enable verbose table debug */
  36. #undef TABLE_DEBUG
  37.  
  38. /**
  39.  * Container for border values during table border calculations
  40.  */
  41. struct border {
  42.         enum css_border_style_e style;  /**< border-style */
  43.         enum css_border_color_e color;  /**< border-color type */
  44.         css_color c;                    /**< border-color value */
  45.         css_fixed width;                /**< border-width length */
  46.         css_unit unit;                  /**< border-width units */
  47. };
  48.  
  49. static void table_used_left_border_for_cell(struct box *cell);
  50. static void table_used_top_border_for_cell(struct box *cell);
  51. static void table_used_right_border_for_cell(struct box *cell);
  52. static void table_used_bottom_border_for_cell(struct box *cell);
  53. static bool table_border_is_more_eyecatching(const struct border *a,
  54.                 box_type a_src, const struct border *b, box_type b_src);
  55. static void table_cell_top_process_table(struct box *table, struct border *a,
  56.                 box_type *a_src);
  57. static bool table_cell_top_process_group(struct box *cell, struct box *group,
  58.                 struct border *a, box_type *a_src);
  59. static bool table_cell_top_process_row(struct box *cell, struct box *row,
  60.                 struct border *a, box_type *a_src);
  61.  
  62.  
  63. /**
  64.  * Determine the column width types for a table.
  65.  *
  66.  * \param  table  box of type BOX_TABLE
  67.  * \return  true on success, false on memory exhaustion
  68.  *
  69.  * The table->col array is allocated and type and width are filled in for each
  70.  * column.
  71.  */
  72.  
  73. bool table_calculate_column_types(struct box *table)
  74. {
  75.         unsigned int i, j;
  76.         struct column *col;
  77.         struct box *row_group, *row, *cell;
  78.  
  79.         if (table->col)
  80.                 /* table->col already constructed, for example frameset table */
  81.                 return true;
  82.  
  83.         table->col = col = talloc_array(table, struct column, table->columns);
  84.         if (!col)
  85.                 return false;
  86.  
  87.         for (i = 0; i != table->columns; i++) {
  88.                 col[i].type = COLUMN_WIDTH_UNKNOWN;
  89.                 col[i].width = 0;
  90.                 col[i].positioned = true;
  91.         }
  92.  
  93.         /* 1st pass: cells with colspan 1 only */
  94.         for (row_group = table->children; row_group; row_group =row_group->next)
  95.         for (row = row_group->children; row; row = row->next)
  96.         for (cell = row->children; cell; cell = cell->next) {
  97.                 enum css_width_e type;
  98.                 css_fixed value = 0;
  99.                 css_unit unit = CSS_UNIT_PX;
  100.  
  101.                 assert(cell->type == BOX_TABLE_CELL);
  102.                 assert(cell->style);
  103.  
  104.                 if (cell->columns != 1)
  105.                         continue;
  106.                 i = cell->start_column;
  107.  
  108.                 if (css_computed_position(cell->style) !=
  109.                                 CSS_POSITION_ABSOLUTE &&
  110.                                 css_computed_position(cell->style) !=
  111.                                 CSS_POSITION_FIXED) {
  112.                         col[i].positioned = false;
  113.                 }
  114.  
  115.                 type = css_computed_width(cell->style, &value, &unit);
  116.  
  117.                 /* fixed width takes priority over any other width type */
  118.                 if (col[i].type != COLUMN_WIDTH_FIXED &&
  119.                                 type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT) {
  120.                         col[i].type = COLUMN_WIDTH_FIXED;
  121.                         col[i].width = FIXTOINT(nscss_len2px(value, unit,
  122.                                         cell->style));
  123.                         if (col[i].width < 0)
  124.                                 col[i].width = 0;
  125.                         continue;
  126.                 }
  127.  
  128.                 if (col[i].type != COLUMN_WIDTH_UNKNOWN)
  129.                         continue;
  130.  
  131.                 if (type == CSS_WIDTH_SET && unit == CSS_UNIT_PCT) {
  132.                         col[i].type = COLUMN_WIDTH_PERCENT;
  133.                         col[i].width = FIXTOINT(value);
  134.                         if (col[i].width < 0)
  135.                                 col[i].width = 0;
  136.                 } else if (type == CSS_WIDTH_AUTO) {
  137.                         col[i].type = COLUMN_WIDTH_AUTO;
  138.                 }
  139.         }
  140.  
  141.         /* 2nd pass: cells which span multiple columns */
  142.         for (row_group = table->children; row_group; row_group =row_group->next)
  143.         for (row = row_group->children; row; row = row->next)
  144.         for (cell = row->children; cell; cell = cell->next) {
  145.                 unsigned int fixed_columns = 0, percent_columns = 0,
  146.                                 auto_columns = 0, unknown_columns = 0;
  147.                 int fixed_width = 0, percent_width = 0;
  148.                 enum css_width_e type;
  149.                 css_fixed value = 0;
  150.                 css_unit unit = CSS_UNIT_PX;
  151.  
  152.                 if (cell->columns == 1)
  153.                         continue;
  154.                 i = cell->start_column;
  155.  
  156.                 for (j = i; j < i + cell->columns; j++) {
  157.                         col[j].positioned = false;
  158.                 }
  159.  
  160.                 /* count column types in spanned cells */
  161.                 for (j = 0; j != cell->columns; j++) {
  162.                         if (col[i + j].type == COLUMN_WIDTH_FIXED) {
  163.                                 fixed_width += col[i + j].width;
  164.                                 fixed_columns++;
  165.                         } else if (col[i + j].type == COLUMN_WIDTH_PERCENT) {
  166.                                 percent_width += col[i + j].width;
  167.                                 percent_columns++;
  168.                         } else if (col[i + j].type == COLUMN_WIDTH_AUTO) {
  169.                                 auto_columns++;
  170.                         } else {
  171.                                 unknown_columns++;
  172.                         }
  173.                 }
  174.  
  175.                 if (!unknown_columns)
  176.                         continue;
  177.  
  178.                 type = css_computed_width(cell->style, &value, &unit);
  179.  
  180.                 /* if cell is fixed width, and all spanned columns are fixed
  181.                  * or unknown width, split extra width among unknown columns */
  182.                 if (type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT &&
  183.                                 fixed_columns + unknown_columns ==
  184.                                 cell->columns) {
  185.                         int width = (FIXTOFLT(nscss_len2px(value, unit,
  186.                                         cell->style)) - fixed_width) /
  187.                                         unknown_columns;
  188.                         if (width < 0)
  189.                                 width = 0;
  190.                         for (j = 0; j != cell->columns; j++) {
  191.                                 if (col[i + j].type == COLUMN_WIDTH_UNKNOWN) {
  192.                                         col[i + j].type = COLUMN_WIDTH_FIXED;
  193.                                         col[i + j].width = width;
  194.                                 }
  195.                         }
  196.                 }
  197.  
  198.                 /* as above for percentage width */
  199.                 if (type == CSS_WIDTH_SET && unit == CSS_UNIT_PCT &&
  200.                                 percent_columns + unknown_columns ==
  201.                                 cell->columns) {
  202.                         int width = (FIXTOFLT(value) -
  203.                                         percent_width) / unknown_columns;
  204.                         if (width < 0)
  205.                                 width = 0;
  206.                         for (j = 0; j != cell->columns; j++) {
  207.                                 if (col[i + j].type == COLUMN_WIDTH_UNKNOWN) {
  208.                                         col[i + j].type = COLUMN_WIDTH_PERCENT;
  209.                                         col[i + j].width = width;
  210.                                 }
  211.                         }
  212.                 }
  213.         }
  214.  
  215.         /* use AUTO if no width type was specified */
  216.         for (i = 0; i != table->columns; i++) {
  217.                 if (col[i].type == COLUMN_WIDTH_UNKNOWN)
  218.                         col[i].type = COLUMN_WIDTH_AUTO;
  219.         }
  220.  
  221. #ifdef TABLE_DEBUG
  222.         for (i = 0; i != table->columns; i++)
  223.                 LOG(("table %p, column %u: type %s, width %i", table, i,
  224.                                 ((const char *[]) {"UNKNOWN", "FIXED", "AUTO",
  225.                                 "PERCENT", "RELATIVE"})[col[i].type],
  226.                                 col[i].width));
  227. #endif
  228.  
  229.         return true;
  230. }
  231.  
  232. /**
  233.  * Calculate used values of border-{trbl}-{style,color,width} for table cells.
  234.  *
  235.  * \param cell  Table cell to consider
  236.  *
  237.  * \post \a cell's border array is populated
  238.  */
  239. void table_used_border_for_cell(struct box *cell)
  240. {
  241.         int side;
  242.  
  243.         assert(cell->type == BOX_TABLE_CELL);
  244.  
  245.         if (css_computed_border_collapse(cell->style) ==
  246.                         CSS_BORDER_COLLAPSE_SEPARATE) {
  247.                 css_fixed width = 0;
  248.                 css_unit unit = CSS_UNIT_PX;
  249.  
  250.                 /* Left border */
  251.                 cell->border[LEFT].style =
  252.                                 css_computed_border_left_style(cell->style);
  253.                 css_computed_border_left_color(cell->style,
  254.                                 &cell->border[LEFT].c);
  255.                 css_computed_border_left_width(cell->style, &width, &unit);
  256.                 cell->border[LEFT].width =
  257.                         FIXTOINT(nscss_len2px(width, unit, cell->style));
  258.  
  259.                 /* Top border */
  260.                 cell->border[TOP].style =
  261.                                 css_computed_border_top_style(cell->style);
  262.                 css_computed_border_top_color(cell->style,
  263.                                 &cell->border[TOP].c);
  264.                 css_computed_border_top_width(cell->style, &width, &unit);
  265.                 cell->border[TOP].width =
  266.                         FIXTOINT(nscss_len2px(width, unit, cell->style));
  267.  
  268.                 /* Right border */
  269.                 cell->border[RIGHT].style =
  270.                                 css_computed_border_right_style(cell->style);
  271.                 css_computed_border_right_color(cell->style,
  272.                                 &cell->border[RIGHT].c);
  273.                 css_computed_border_right_width(cell->style, &width, &unit);
  274.                 cell->border[RIGHT].width =
  275.                         FIXTOINT(nscss_len2px(width, unit, cell->style));
  276.  
  277.                 /* Bottom border */
  278.                 cell->border[BOTTOM].style =
  279.                                 css_computed_border_bottom_style(cell->style);
  280.                 css_computed_border_bottom_color(cell->style,
  281.                                 &cell->border[BOTTOM].c);
  282.                 css_computed_border_bottom_width(cell->style, &width, &unit);
  283.                 cell->border[BOTTOM].width =
  284.                         FIXTOINT(nscss_len2px(width, unit, cell->style));
  285.         } else {
  286.                 /* Left border */
  287.                 table_used_left_border_for_cell(cell);
  288.  
  289.                 /* Top border */
  290.                 table_used_top_border_for_cell(cell);
  291.  
  292.                 /* Right border */
  293.                 table_used_right_border_for_cell(cell);
  294.  
  295.                 /* Bottom border */
  296.                 table_used_bottom_border_for_cell(cell);
  297.         }
  298.  
  299.         /* Finally, ensure that any borders configured as
  300.          * hidden or none have zero width. (c.f. layout_find_dimensions) */
  301.         for (side = 0; side != 4; side++) {
  302.                 if (cell->border[side].style == CSS_BORDER_STYLE_HIDDEN ||
  303.                                 cell->border[side].style ==
  304.                                 CSS_BORDER_STYLE_NONE)
  305.                         cell->border[side].width = 0;
  306.         }
  307. }
  308.  
  309. /******************************************************************************
  310.  * Helpers for used border calculations                                       *
  311.  ******************************************************************************/
  312.  
  313. /**
  314.  * Calculate used values of border-left-{style,color,width}
  315.  *
  316.  * \param cell Table cell to consider
  317.  */
  318. void table_used_left_border_for_cell(struct box *cell)
  319. {
  320.         struct border a, b;
  321.         box_type a_src, b_src;
  322.  
  323.         /** \todo Need column and column_group, too */
  324.  
  325.         /* Initialise to computed left border for cell */
  326.         a.style = css_computed_border_left_style(cell->style);
  327.         a.color = css_computed_border_left_color(cell->style, &a.c);
  328.         css_computed_border_left_width(cell->style, &a.width, &a.unit);
  329.         a.width = nscss_len2px(a.width, a.unit, cell->style);
  330.         a.unit = CSS_UNIT_PX;
  331.         a_src = BOX_TABLE_CELL;
  332.  
  333.         if (cell->prev != NULL || cell->start_column != 0) {
  334.                 /* Cell to the left -- consider its right border */
  335.                 struct box *prev = NULL;
  336.  
  337.                 if (cell->prev == NULL) {
  338.                         struct box *row;
  339.  
  340.                         /* Spanned from a previous row */
  341.                         for (row = cell->parent; row != NULL; row = row->prev) {
  342.                                 for (prev = row->children; prev != NULL;
  343.                                                 prev = prev->next) {
  344.                                         if (prev->start_column +
  345.                                                         prev->columns ==
  346.                                                         cell->start_column)
  347.                                                 break;
  348.                                 }
  349.  
  350.                                 if (prev != NULL)
  351.                                         break;
  352.                         }
  353.  
  354.                         assert(prev != NULL);
  355.                 } else {
  356.                         prev = cell->prev;
  357.                 }
  358.  
  359.                 b.style = css_computed_border_right_style(prev->style);
  360.                 b.color = css_computed_border_right_color(prev->style, &b.c);
  361.                 css_computed_border_right_width(prev->style, &b.width, &b.unit);
  362.                 b.width = nscss_len2px(b.width, b.unit, prev->style);
  363.                 b.unit = CSS_UNIT_PX;
  364.                 b_src = BOX_TABLE_CELL;
  365.  
  366.                 if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) {
  367.                         a = b;
  368.                         a_src = b_src;
  369.                 }
  370.         } else {
  371.                 /* First cell in row, so consider rows and row group */
  372.                 struct box *row = cell->parent;
  373.                 struct box *group = row->parent;
  374.                 struct box *table = group->parent;
  375.                 unsigned int rows = cell->rows;
  376.  
  377.                 while (rows-- > 0 && row != NULL) {
  378.                         /* Spanned rows -- consider their left border */
  379.                         b.style = css_computed_border_left_style(row->style);
  380.                         b.color = css_computed_border_left_color(
  381.                                         row->style, &b.c);
  382.                         css_computed_border_left_width(
  383.                                         row->style, &b.width, &b.unit);
  384.                         b.width = nscss_len2px(b.width, b.unit, row->style);
  385.                         b.unit = CSS_UNIT_PX;
  386.                         b_src = BOX_TABLE_ROW;
  387.                
  388.                         if (table_border_is_more_eyecatching(&a, a_src,
  389.                                         &b, b_src)) {
  390.                                 a = b;
  391.                                 a_src = b_src;
  392.                         }
  393.  
  394.                         row = row->next;
  395.                 }
  396.  
  397.                 /** \todo can cells span row groups? */
  398.  
  399.                 /* Row group -- consider its left border */
  400.                 b.style = css_computed_border_left_style(group->style);
  401.                 b.color = css_computed_border_left_color(group->style, &b.c);
  402.                 css_computed_border_left_width(group->style, &b.width, &b.unit);
  403.                 b.width = nscss_len2px(b.width, b.unit, group->style);
  404.                 b.unit = CSS_UNIT_PX;
  405.                 b_src = BOX_TABLE_ROW_GROUP;
  406.                
  407.                 if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) {
  408.                         a = b;
  409.                         a_src = b_src;
  410.                 }
  411.  
  412.                 /* The table itself -- consider its left border */
  413.                 b.style = css_computed_border_left_style(table->style);
  414.                 b.color = css_computed_border_left_color(table->style, &b.c);
  415.                 css_computed_border_left_width(table->style, &b.width, &b.unit);
  416.                 b.width = nscss_len2px(b.width, b.unit, table->style);
  417.                 b.unit = CSS_UNIT_PX;
  418.                 b_src = BOX_TABLE;
  419.                
  420.                 if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) {
  421.                         a = b;
  422.                         a_src = b_src;
  423.                 }
  424.         }
  425.  
  426.         /* a now contains the used left border for the cell */
  427.         cell->border[LEFT].style = a.style;
  428.         cell->border[LEFT].c = a.c;
  429.         cell->border[LEFT].width =
  430.                         FIXTOINT(nscss_len2px(a.width, a.unit, cell->style));
  431. }
  432.  
  433. /**
  434.  * Calculate used values of border-top-{style,color,width}
  435.  *
  436.  * \param cell Table cell to consider
  437.  */
  438. void table_used_top_border_for_cell(struct box *cell)
  439. {
  440.         struct border a, b;
  441.         box_type a_src, b_src;
  442.         struct box *row = cell->parent;
  443.         bool process_group = false;
  444.  
  445.         /* Initialise to computed top border for cell */
  446.         a.style = css_computed_border_top_style(cell->style);
  447.         css_computed_border_top_color(cell->style, &a.c);
  448.         css_computed_border_top_width(cell->style, &a.width, &a.unit);
  449.         a.width = nscss_len2px(a.width, a.unit, cell->style);
  450.         a.unit = CSS_UNIT_PX;
  451.         a_src = BOX_TABLE_CELL;
  452.  
  453.         /* Top border of row */
  454.         b.style = css_computed_border_top_style(row->style);
  455.         css_computed_border_top_color(row->style, &b.c);
  456.         css_computed_border_top_width(row->style, &b.width, &b.unit);
  457.         b.width = nscss_len2px(b.width, b.unit, row->style);
  458.         b.unit = CSS_UNIT_PX;
  459.         b_src = BOX_TABLE_ROW;
  460.  
  461.         if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) {
  462.                 a = b;
  463.                 a_src = b_src;
  464.         }
  465.  
  466.         if (row->prev != NULL) {
  467.                 /* Consider row(s) above */
  468.                 while (table_cell_top_process_row(cell, row->prev,
  469.                                 &a, &a_src) == false) {
  470.                         if (row->prev->prev == NULL) {
  471.                                 /* Consider row group */
  472.                                 process_group = true;
  473.                                 break;
  474.                         } else {
  475.                                 row = row->prev;
  476.                         }
  477.                 }
  478.         } else {
  479.                 process_group = true;
  480.         }
  481.  
  482.         if (process_group) {
  483.                 struct box *group = row->parent;
  484.  
  485.                 /* Top border of row group */
  486.                 b.style = css_computed_border_top_style(group->style);
  487.                 b.color = css_computed_border_top_color(group->style, &b.c);
  488.                 css_computed_border_top_width(group->style, &b.width, &b.unit);
  489.                 b.width = nscss_len2px(b.width, b.unit, group->style);
  490.                 b.unit = CSS_UNIT_PX;
  491.                 b_src = BOX_TABLE_ROW_GROUP;
  492.  
  493.                 if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) {
  494.                         a = b;
  495.                         a_src = b_src;
  496.                 }
  497.  
  498.                 if (group->prev == NULL) {
  499.                         /* Top border of table */
  500.                         table_cell_top_process_table(group->parent, &a, &a_src);
  501.                 } else {
  502.                         /* Process previous group(s) */
  503.                         while (table_cell_top_process_group(cell, group->prev,
  504.                                         &a, &a_src) == false) {
  505.                                 if (group->prev->prev == NULL) {
  506.                                         /* Top border of table */
  507.                                         table_cell_top_process_table(
  508.                                                         group->parent,
  509.                                                         &a, &a_src);
  510.                                         break;
  511.                                 } else {
  512.                                         group = group->prev;
  513.                                 }
  514.                         }
  515.                 }
  516.         }
  517.  
  518.         /* a now contains the used top border for the cell */
  519.         cell->border[TOP].style = a.style;
  520.         cell->border[TOP].c = a.c;
  521.         cell->border[TOP].width =
  522.                         FIXTOINT(nscss_len2px(a.width, a.unit, cell->style));
  523. }
  524.  
  525. /**
  526.  * Calculate used values of border-right-{style,color,width}
  527.  *
  528.  * \param cell Table cell to consider
  529.  */
  530. void table_used_right_border_for_cell(struct box *cell)
  531. {
  532.         struct border a, b;
  533.         box_type a_src, b_src;
  534.  
  535.         /** \todo Need column and column_group, too */
  536.  
  537.         /* Initialise to computed right border for cell */
  538.         a.style = css_computed_border_right_style(cell->style);
  539.         css_computed_border_right_color(cell->style, &a.c);
  540.         css_computed_border_right_width(cell->style, &a.width, &a.unit);
  541.         a.width = nscss_len2px(a.width, a.unit, cell->style);
  542.         a.unit = CSS_UNIT_PX;
  543.         a_src = BOX_TABLE_CELL;
  544.  
  545.         if (cell->next != NULL || cell->start_column + cell->columns !=
  546.                         cell->parent->parent->parent->columns) {
  547.                 /* Cell is not at right edge of table -- no right border */
  548.                 a.style = CSS_BORDER_STYLE_NONE;
  549.                 a.width = 0;
  550.                 a.unit = CSS_UNIT_PX;
  551.         } else {
  552.                 /* Last cell in row, so consider rows and row group */
  553.                 struct box *row = cell->parent;
  554.                 struct box *group = row->parent;
  555.                 struct box *table = group->parent;
  556.                 unsigned int rows = cell->rows;
  557.  
  558.                 while (rows-- > 0 && row != NULL) {
  559.                         /* Spanned rows -- consider their right border */
  560.                         b.style = css_computed_border_right_style(row->style);
  561.                         b.color = css_computed_border_right_color(
  562.                                         row->style, &b.c);
  563.                         css_computed_border_right_width(
  564.                                         row->style, &b.width, &b.unit);
  565.                         b.width = nscss_len2px(b.width, b.unit, row->style);
  566.                         b.unit = CSS_UNIT_PX;
  567.                         b_src = BOX_TABLE_ROW;
  568.                
  569.                         if (table_border_is_more_eyecatching(&a, a_src,
  570.                                         &b, b_src)) {
  571.                                 a = b;
  572.                                 a_src = b_src;
  573.                         }
  574.  
  575.                         row = row->next;
  576.                 }
  577.  
  578.                 /** \todo can cells span row groups? */
  579.  
  580.                 /* Row group -- consider its right border */
  581.                 b.style = css_computed_border_right_style(group->style);
  582.                 b.color = css_computed_border_right_color(group->style, &b.c);
  583.                 css_computed_border_right_width(group->style,
  584.                                 &b.width, &b.unit);
  585.                 b.width = nscss_len2px(b.width, b.unit, group->style);
  586.                 b.unit = CSS_UNIT_PX;
  587.                 b_src = BOX_TABLE_ROW_GROUP;
  588.                
  589.                 if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) {
  590.                         a = b;
  591.                         a_src = b_src;
  592.                 }
  593.  
  594.                 /* The table itself -- consider its right border */
  595.                 b.style = css_computed_border_right_style(table->style);
  596.                 b.color = css_computed_border_right_color(table->style, &b.c);
  597.                 css_computed_border_right_width(table->style,
  598.                                 &b.width, &b.unit);
  599.                 b.width = nscss_len2px(b.width, b.unit, table->style);
  600.                 b.unit = CSS_UNIT_PX;
  601.                 b_src = BOX_TABLE;
  602.                
  603.                 if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) {
  604.                         a = b;
  605.                         a_src = b_src;
  606.                 }
  607.         }
  608.  
  609.         /* a now contains the used right border for the cell */
  610.         cell->border[RIGHT].style = a.style;
  611.         cell->border[RIGHT].c = a.c;
  612.         cell->border[RIGHT].width =
  613.                         FIXTOINT(nscss_len2px(a.width, a.unit, cell->style));
  614. }
  615.  
  616. /**
  617.  * Calculate used values of border-bottom-{style,color,width}
  618.  *
  619.  * \param cell Table cell to consider
  620.  */
  621. void table_used_bottom_border_for_cell(struct box *cell)
  622. {
  623.         struct border a, b;
  624.         box_type a_src, b_src;
  625.         struct box *row = cell->parent;
  626.         unsigned int rows = cell->rows;
  627.  
  628.         /* Initialise to computed bottom border for cell */
  629.         a.style = css_computed_border_bottom_style(cell->style);
  630.         css_computed_border_bottom_color(cell->style, &a.c);
  631.         css_computed_border_bottom_width(cell->style, &a.width, &a.unit);
  632.         a.width = nscss_len2px(a.width, a.unit, cell->style);
  633.         a.unit = CSS_UNIT_PX;
  634.         a_src = BOX_TABLE_CELL;
  635.  
  636.         while (rows-- > 0 && row != NULL)
  637.                 row = row->next;
  638.  
  639.         /** \todo Can cells span row groups? */
  640.  
  641.         if (row != NULL) {
  642.                 /* Cell is not at bottom edge of table -- no bottom border */
  643.                 a.style = CSS_BORDER_STYLE_NONE;
  644.                 a.width = 0;
  645.                 a.unit = CSS_UNIT_PX;
  646.         } else {
  647.                 /* Cell at bottom of table, so consider row and row group */
  648.                 struct box *row = cell->parent;
  649.                 struct box *group = row->parent;
  650.                 struct box *table = group->parent;
  651.  
  652.                 /* Bottom border of row */
  653.                 b.style = css_computed_border_bottom_style(row->style);
  654.                 b.color = css_computed_border_bottom_color(row->style, &b.c);
  655.                 css_computed_border_bottom_width(row->style, &b.width, &b.unit);
  656.                 b.width = nscss_len2px(b.width, b.unit, row->style);
  657.                 b.unit = CSS_UNIT_PX;
  658.                 b_src = BOX_TABLE_ROW;
  659.                
  660.                 if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) {
  661.                         a = b;
  662.                         a_src = b_src;
  663.                 }
  664.  
  665.                 /* Row group -- consider its bottom border */
  666.                 b.style = css_computed_border_bottom_style(group->style);
  667.                 b.color = css_computed_border_bottom_color(group->style, &b.c);
  668.                 css_computed_border_bottom_width(group->style,
  669.                                 &b.width, &b.unit);
  670.                 b.width = nscss_len2px(b.width, b.unit, group->style);
  671.                 b.unit = CSS_UNIT_PX;
  672.                 b_src = BOX_TABLE_ROW_GROUP;
  673.                
  674.                 if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) {
  675.                         a = b;
  676.                         a_src = b_src;
  677.                 }
  678.  
  679.                 /* The table itself -- consider its bottom border */
  680.                 b.style = css_computed_border_bottom_style(table->style);
  681.                 b.color = css_computed_border_bottom_color(table->style, &b.c);
  682.                 css_computed_border_bottom_width(table->style,
  683.                                 &b.width, &b.unit);
  684.                 b.width = nscss_len2px(b.width, b.unit, table->style);
  685.                 b.unit = CSS_UNIT_PX;
  686.                 b_src = BOX_TABLE;
  687.                
  688.                 if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) {
  689.                         a = b;
  690.                         a_src = b_src;
  691.                 }
  692.         }
  693.  
  694.         /* a now contains the used bottom border for the cell */
  695.         cell->border[BOTTOM].style = a.style;
  696.         cell->border[BOTTOM].c = a.c;
  697.         cell->border[BOTTOM].width =
  698.                         FIXTOINT(nscss_len2px(a.width, a.unit, cell->style));
  699. }
  700.  
  701. /**
  702.  * Determine if a border style is more eyecatching than another
  703.  *
  704.  * \param a      Reference border style
  705.  * \param a_src  Source of \a a
  706.  * \param b      Candidate border style
  707.  * \param b_src  Source of \a b
  708.  * \return True if \a b is more eyecatching than \a a
  709.  */
  710. bool table_border_is_more_eyecatching(const struct border *a,
  711.                 box_type a_src, const struct border *b, box_type b_src)
  712. {
  713.         css_fixed awidth, bwidth;
  714.         int impact = 0;
  715.  
  716.         /* See CSS 2.1 $17.6.2.1 */
  717.  
  718.         /* 1 + 2 -- hidden beats everything, none beats nothing */
  719.         if (a->style == CSS_BORDER_STYLE_HIDDEN ||
  720.                         b->style == CSS_BORDER_STYLE_NONE)
  721.                 return false;
  722.  
  723.         if (b->style == CSS_BORDER_STYLE_HIDDEN ||
  724.                         a->style == CSS_BORDER_STYLE_NONE)
  725.                 return true;
  726.  
  727.         /* 3a -- wider borders beat narrow ones */
  728.         /* The widths must be absolute, which will be the case
  729.          * if they've come from a computed style. */
  730.         assert(a->unit != CSS_UNIT_EM && a->unit != CSS_UNIT_EX);
  731.         assert(b->unit != CSS_UNIT_EM && b->unit != CSS_UNIT_EX);
  732.         awidth = nscss_len2px(a->width, a->unit, NULL);
  733.         bwidth = nscss_len2px(b->width, b->unit, NULL);
  734.  
  735.         if (awidth < bwidth)
  736.                 return true;
  737.         else if (bwidth < awidth)
  738.                 return false;
  739.  
  740.         /* 3b -- sort by style */
  741.         switch (a->style) {
  742.         case CSS_BORDER_STYLE_DOUBLE: impact++;
  743.         case CSS_BORDER_STYLE_SOLID:  impact++;
  744.         case CSS_BORDER_STYLE_DASHED: impact++;
  745.         case CSS_BORDER_STYLE_DOTTED: impact++;
  746.         case CSS_BORDER_STYLE_RIDGE:  impact++;
  747.         case CSS_BORDER_STYLE_OUTSET: impact++;
  748.         case CSS_BORDER_STYLE_GROOVE: impact++;
  749.         case CSS_BORDER_STYLE_INSET:  impact++;
  750.         default:
  751.                 break;
  752.         }
  753.  
  754.         switch (b->style) {
  755.         case CSS_BORDER_STYLE_DOUBLE: impact--;
  756.         case CSS_BORDER_STYLE_SOLID:  impact--;
  757.         case CSS_BORDER_STYLE_DASHED: impact--;
  758.         case CSS_BORDER_STYLE_DOTTED: impact--;
  759.         case CSS_BORDER_STYLE_RIDGE:  impact--;
  760.         case CSS_BORDER_STYLE_OUTSET: impact--;
  761.         case CSS_BORDER_STYLE_GROOVE: impact--;
  762.         case CSS_BORDER_STYLE_INSET:  impact--;
  763.         default:
  764.                 break;
  765.         }
  766.  
  767.         if (impact < 0)
  768.                 return true;
  769.         else if (impact > 0)
  770.                 return false;
  771.  
  772.         /* 4a -- sort by origin */
  773.         impact = 0;
  774.  
  775.         switch (a_src) {
  776.         case BOX_TABLE_CELL:       impact++;
  777.         case BOX_TABLE_ROW:        impact++;
  778.         case BOX_TABLE_ROW_GROUP:  impact++;
  779.         /** \todo COL/COL_GROUP */
  780.         case BOX_TABLE:            impact++;
  781.         default:
  782.                 break;
  783.         }
  784.  
  785.         switch (b_src) {
  786.         case BOX_TABLE_CELL:       impact--;
  787.         case BOX_TABLE_ROW:        impact--;
  788.         case BOX_TABLE_ROW_GROUP:  impact--;
  789.         /** \todo COL/COL_GROUP */
  790.         case BOX_TABLE:            impact--;
  791.         default:
  792.                 break;
  793.         }
  794.  
  795.         if (impact < 0)
  796.                 return true;
  797.         else if (impact > 0)
  798.                 return false;
  799.  
  800.         /* 4b -- furthest left (if direction: ltr) and towards top wins */
  801.         /** \todo Currently assumes b satisifies this */
  802.         return true;
  803. }
  804.  
  805. /******************************************************************************
  806.  * Helpers for top border collapsing                                          *
  807.  ******************************************************************************/
  808.  
  809. /**
  810.  * Process a table
  811.  *
  812.  * \param table  Table to process
  813.  * \param a      Current border style for cell
  814.  * \param a_src  Source of \a a
  815.  *
  816.  * \post \a a will be updated with most eyecatching style
  817.  * \post \a a_src will be updated also
  818.  */
  819. void table_cell_top_process_table(struct box *table, struct border *a,
  820.                 box_type *a_src)
  821. {
  822.         struct border b;
  823.         box_type b_src;
  824.  
  825.         /* Top border of table */
  826.         b.style = css_computed_border_top_style(table->style);
  827.         b.color = css_computed_border_top_color(table->style, &b.c);
  828.         css_computed_border_top_width(table->style, &b.width, &b.unit);
  829.         b.width = nscss_len2px(b.width, b.unit, table->style);
  830.         b.unit = CSS_UNIT_PX;
  831.         b_src = BOX_TABLE;
  832.  
  833.         if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) {
  834.                 *a = b;
  835.                 *a_src = b_src;
  836.         }
  837. }
  838.  
  839. /**
  840.  * Process a group
  841.  *
  842.  * \param cell   Cell being considered
  843.  * \param group  Group to process
  844.  * \param a      Current border style for cell
  845.  * \param a_src  Source of \a a
  846.  * \return true if group has non-empty rows, false otherwise
  847.  *
  848.  * \post \a a will be updated with most eyecatching style
  849.  * \post \a a_src will be updated also
  850.  */
  851. bool table_cell_top_process_group(struct box *cell, struct box *group,
  852.                 struct border *a, box_type *a_src)
  853. {
  854.         struct border b;
  855.         box_type b_src;
  856.  
  857.         /* Bottom border of group */
  858.         b.style = css_computed_border_bottom_style(group->style);
  859.         b.color = css_computed_border_bottom_color(group->style, &b.c);
  860.         css_computed_border_bottom_width(group->style, &b.width, &b.unit);
  861.         b.width = nscss_len2px(b.width, b.unit, group->style);
  862.         b.unit = CSS_UNIT_PX;
  863.         b_src = BOX_TABLE_ROW_GROUP;
  864.  
  865.         if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) {
  866.                 *a = b;
  867.                 *a_src = b_src;
  868.         }
  869.  
  870.         if (group->last != NULL) {
  871.                 /* Process rows in group, starting with last */
  872.                 struct box *row = group->last;
  873.  
  874.                 while (table_cell_top_process_row(cell, row,
  875.                                 a, a_src) == false) {
  876.                         if (row->prev == NULL) {
  877.                                 return false;
  878.                         } else {
  879.                                 row = row->prev;
  880.                         }
  881.                 }
  882.         } else {
  883.                 /* Group is empty, so consider its top border */
  884.                 b.style = css_computed_border_top_style(group->style);
  885.                 b.color = css_computed_border_top_color(group->style, &b.c);
  886.                 css_computed_border_top_width(group->style, &b.width, &b.unit);
  887.                 b.width = nscss_len2px(b.width, b.unit, group->style);
  888.                 b.unit = CSS_UNIT_PX;
  889.                 b_src = BOX_TABLE_ROW_GROUP;
  890.  
  891.                 if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) {
  892.                         *a = b;
  893.                         *a_src = b_src;
  894.                 }
  895.  
  896.                 return false;
  897.         }
  898.  
  899.         return true;
  900. }
  901.  
  902. /**
  903.  * Process a row
  904.  *
  905.  * \param cell   Cell being considered
  906.  * \param row    Row to process
  907.  * \param a      Current border style for cell
  908.  * \param a_src  Source of \a a
  909.  * \return true if row has cells, false otherwise
  910.  *
  911.  * \post \a a will be updated with most eyecatching style
  912.  * \post \a a_src will be updated also
  913.  */
  914. bool table_cell_top_process_row(struct box *cell, struct box *row,
  915.                 struct border *a, box_type *a_src)
  916. {
  917.         struct border b;
  918.         box_type b_src;
  919.  
  920.         /* Bottom border of row */
  921.         b.style = css_computed_border_bottom_style(row->style);
  922.         b.color = css_computed_border_bottom_color(row->style, &b.c);
  923.         css_computed_border_bottom_width(row->style, &b.width, &b.unit);
  924.         b.width = nscss_len2px(b.width, b.unit, row->style);
  925.         b.unit = CSS_UNIT_PX;
  926.         b_src = BOX_TABLE_ROW;
  927.  
  928.         if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) {
  929.                 *a = b;
  930.                 *a_src = b_src;
  931.         }
  932.  
  933.         if (row->children == NULL) {
  934.                 /* Row is empty, so consider its top border */
  935.                 b.style = css_computed_border_top_style(row->style);
  936.                 b.color = css_computed_border_top_color(row->style, &b.c);
  937.                 css_computed_border_top_width(row->style, &b.width, &b.unit);
  938.                 b.width = nscss_len2px(b.width, b.unit, row->style);
  939.                 b.unit = CSS_UNIT_PX;
  940.                 b_src = BOX_TABLE_ROW;
  941.  
  942.                 if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) {
  943.                         *a = b;
  944.                         *a_src = b_src;
  945.                 }
  946.  
  947.                 return false;
  948.         } else {
  949.                 /* Process cells that are directly above the cell being
  950.                  * considered. They may not be in this row, but in one of the
  951.                  * rows above it in the case where rowspan > 1. */
  952.                 struct box *c;
  953.                 bool processed = false;
  954.  
  955.                 while (processed == false) {
  956.                         for (c = row->children; c != NULL; c = c->next) {
  957.                                 /* Ignore cells to the left */
  958.                                 if (c->start_column + c->columns <
  959.                                                 cell->start_column)
  960.                                         continue;
  961.                                 /* Ignore cells to the right */
  962.                                 if (c->start_column >= cell->start_column +
  963.                                                 cell->columns)
  964.                                         continue;
  965.  
  966.                                 /* Flag that we've processed a cell */
  967.                                 processed = true;
  968.  
  969.                                 /* Consider bottom border */
  970.                                 b.style = css_computed_border_bottom_style(
  971.                                                 c->style);
  972.                                 b.color = css_computed_border_bottom_color(
  973.                                                 c->style, &b.c);
  974.                                 css_computed_border_bottom_width(c->style,
  975.                                                 &b.width, &b.unit);
  976.                                 b.width = nscss_len2px(b.width, b.unit,
  977.                                                 c->style);
  978.                                 b.unit = CSS_UNIT_PX;
  979.                                 b_src = BOX_TABLE_CELL;
  980.  
  981.                                 if (table_border_is_more_eyecatching(a, *a_src,
  982.                                                 &b, b_src)) {
  983.                                         *a = b;
  984.                                         *a_src = b_src;
  985.                                 }
  986.                         }
  987.  
  988.                         if (processed == false) {
  989.                                 /* There must be a preceding row */
  990.                                 assert(row->prev != NULL);
  991.  
  992.                                 row = row->prev;
  993.                         }
  994.                 }
  995.         }
  996.  
  997.         return true;
  998. }
  999.  
  1000.