785 lines
20 KiB
C
785 lines
20 KiB
C
/*
|
|
* Copyright 2005 James Bursa <bursa@users.sourceforge.net>
|
|
* Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
|
|
* Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk>
|
|
* Copyright 2004 Kevin Bagust <kevin.bagust@ntlworld.com>
|
|
*
|
|
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
|
*
|
|
* NetSurf is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* NetSurf is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/** \file
|
|
* Box tree normalisation (implementation).
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdbool.h>
|
|
#include "css/css.h"
|
|
#include "render/box.h"
|
|
#include "render/table.h"
|
|
#include "desktop/gui.h"
|
|
#define NDEBUG
|
|
#include "utils/log.h"
|
|
#include "utils/talloc.h"
|
|
|
|
|
|
struct span_info {
|
|
unsigned int row_span;
|
|
bool auto_row;
|
|
bool auto_column;
|
|
};
|
|
|
|
struct columns {
|
|
unsigned int current_column;
|
|
bool extra;
|
|
/* Number of columns in main part of table 1..max columns */
|
|
unsigned int num_columns;
|
|
/* Information about columns in main table,
|
|
array 0 to num_columns - 1 */
|
|
struct span_info *spans;
|
|
/* Number of columns that have cells after a colspan 0 */
|
|
unsigned int extra_columns;
|
|
/* Number of rows in table */
|
|
unsigned int num_rows;
|
|
};
|
|
|
|
|
|
static bool box_normalise_table(struct box *table, struct content *c);
|
|
static void box_normalise_table_spans(struct box *table);
|
|
static bool box_normalise_table_row_group(struct box *row_group,
|
|
struct columns *col_info,
|
|
struct content *c);
|
|
static bool box_normalise_table_row(struct box *row,
|
|
struct columns *col_info,
|
|
struct content *c);
|
|
static bool calculate_table_row(struct columns *col_info,
|
|
unsigned int col_span, unsigned int row_span,
|
|
unsigned int *start_column);
|
|
static bool box_normalise_inline_container(struct box *cont, struct content *c);
|
|
|
|
|
|
/**
|
|
* Ensure the box tree is correctly nested by adding and removing nodes.
|
|
*
|
|
* \param block box of type BLOCK, INLINE_BLOCK, or TABLE_CELL
|
|
* \param box_pool pool to allocate new boxes in
|
|
* \return true on success, false on memory exhaustion
|
|
*
|
|
* The tree is modified to satisfy the following:
|
|
* \code
|
|
* parent permitted child nodes
|
|
* BLOCK, INLINE_BLOCK BLOCK, INLINE_CONTAINER, TABLE
|
|
* INLINE_CONTAINER INLINE, INLINE_BLOCK, FLOAT_LEFT, FLOAT_RIGHT, BR, TEXT
|
|
* INLINE, TEXT none
|
|
* TABLE at least 1 TABLE_ROW_GROUP
|
|
* TABLE_ROW_GROUP at least 1 TABLE_ROW
|
|
* TABLE_ROW at least 1 TABLE_CELL
|
|
* TABLE_CELL BLOCK, INLINE_CONTAINER, TABLE (same as BLOCK)
|
|
* FLOAT_(LEFT|RIGHT) exactly 1 BLOCK or TABLE
|
|
* \endcode
|
|
*/
|
|
|
|
bool box_normalise_block(struct box *block, struct content *c)
|
|
{
|
|
struct box *child;
|
|
struct box *next_child;
|
|
struct box *table;
|
|
struct css_style *style;
|
|
|
|
assert(block != 0);
|
|
LOG(("block %p, block->type %u", block, block->type));
|
|
assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK ||
|
|
block->type == BOX_TABLE_CELL);
|
|
gui_multitask();
|
|
|
|
for (child = block->children; child != 0; child = next_child) {
|
|
LOG(("child %p, child->type = %d", child, child->type));
|
|
next_child = child->next; /* child may be destroyed */
|
|
switch (child->type) {
|
|
case BOX_BLOCK:
|
|
/* ok */
|
|
if (!box_normalise_block(child, c))
|
|
return false;
|
|
break;
|
|
case BOX_INLINE_CONTAINER:
|
|
if (!box_normalise_inline_container(child,
|
|
c))
|
|
return false;
|
|
break;
|
|
case BOX_TABLE:
|
|
if (!box_normalise_table(child, c))
|
|
return false;
|
|
break;
|
|
case BOX_INLINE:
|
|
case BOX_INLINE_END:
|
|
case BOX_INLINE_BLOCK:
|
|
case BOX_FLOAT_LEFT:
|
|
case BOX_FLOAT_RIGHT:
|
|
case BOX_BR:
|
|
case BOX_TEXT:
|
|
/* should have been wrapped in inline
|
|
container by convert_xml_to_box() */
|
|
assert(0);
|
|
break;
|
|
case BOX_TABLE_ROW_GROUP:
|
|
case BOX_TABLE_ROW:
|
|
case BOX_TABLE_CELL:
|
|
/* insert implied table */
|
|
style = talloc_memdup(c, block->style, sizeof *style);
|
|
if (!style)
|
|
return false;
|
|
css_cascade(style, &css_blank_style, NULL);
|
|
table = box_create(style, block->href, block->target,
|
|
0, 0, c);
|
|
if (!table) {
|
|
talloc_free(style);
|
|
return false;
|
|
}
|
|
table->type = BOX_TABLE;
|
|
if (child->prev == 0)
|
|
block->children = table;
|
|
else
|
|
child->prev->next = table;
|
|
table->prev = child->prev;
|
|
while (child != 0 && (
|
|
child->type == BOX_TABLE_ROW_GROUP ||
|
|
child->type == BOX_TABLE_ROW ||
|
|
child->type == BOX_TABLE_CELL)) {
|
|
box_add_child(table, child);
|
|
next_child = child->next;
|
|
child->next = 0;
|
|
child = next_child;
|
|
}
|
|
table->last->next = 0;
|
|
table->next = next_child = child;
|
|
if (table->next)
|
|
table->next->prev = table;
|
|
table->parent = block;
|
|
if (!box_normalise_table(table, c))
|
|
return false;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool box_normalise_table(struct box *table, struct content * c)
|
|
{
|
|
struct box *child;
|
|
struct box *next_child;
|
|
struct box *row_group;
|
|
struct css_style *style;
|
|
struct columns col_info;
|
|
|
|
assert(table != 0);
|
|
assert(table->type == BOX_TABLE);
|
|
LOG(("table %p", table));
|
|
col_info.num_columns = 1;
|
|
col_info.current_column = 0;
|
|
col_info.spans = malloc(2 * sizeof *col_info.spans);
|
|
if (!col_info.spans)
|
|
return false;
|
|
col_info.spans[0].row_span = col_info.spans[1].row_span = 0;
|
|
col_info.spans[0].auto_row = col_info.spans[0].auto_column =
|
|
col_info.spans[1].auto_row = col_info.spans[1].auto_column = false;
|
|
col_info.num_rows = col_info.extra_columns = 0;
|
|
col_info.extra = false;
|
|
|
|
for (child = table->children; child != 0; child = next_child) {
|
|
next_child = child->next;
|
|
switch (child->type) {
|
|
case BOX_TABLE_ROW_GROUP:
|
|
/* ok */
|
|
if (!box_normalise_table_row_group(child,
|
|
&col_info, c)) {
|
|
free(col_info.spans);
|
|
return false;
|
|
}
|
|
break;
|
|
case BOX_BLOCK:
|
|
case BOX_INLINE_CONTAINER:
|
|
case BOX_TABLE:
|
|
case BOX_TABLE_ROW:
|
|
case BOX_TABLE_CELL:
|
|
/* insert implied table row group */
|
|
assert(table->style != NULL);
|
|
style = talloc_memdup(c, table->style, sizeof *style);
|
|
if (!style) {
|
|
free(col_info.spans);
|
|
return false;
|
|
}
|
|
css_cascade(style, &css_blank_style, NULL);
|
|
row_group = box_create(style, table->href,
|
|
table->target, 0, 0, c);
|
|
if (!row_group) {
|
|
free(col_info.spans);
|
|
talloc_free(style);
|
|
return false;
|
|
}
|
|
row_group->type = BOX_TABLE_ROW_GROUP;
|
|
if (child->prev == 0)
|
|
table->children = row_group;
|
|
else
|
|
child->prev->next = row_group;
|
|
row_group->prev = child->prev;
|
|
while (child != 0 && (
|
|
child->type == BOX_BLOCK ||
|
|
child->type == BOX_INLINE_CONTAINER ||
|
|
child->type == BOX_TABLE ||
|
|
child->type == BOX_TABLE_ROW ||
|
|
child->type == BOX_TABLE_CELL)) {
|
|
box_add_child(row_group, child);
|
|
next_child = child->next;
|
|
child->next = 0;
|
|
child = next_child;
|
|
}
|
|
assert(row_group->last != NULL);
|
|
row_group->last->next = 0;
|
|
row_group->next = next_child = child;
|
|
if (row_group->next)
|
|
row_group->next->prev = row_group;
|
|
else
|
|
table->last = row_group;
|
|
row_group->parent = table;
|
|
if (!box_normalise_table_row_group(row_group,
|
|
&col_info, c)) {
|
|
free(col_info.spans);
|
|
return false;
|
|
}
|
|
break;
|
|
case BOX_INLINE:
|
|
case BOX_INLINE_END:
|
|
case BOX_INLINE_BLOCK:
|
|
case BOX_FLOAT_LEFT:
|
|
case BOX_FLOAT_RIGHT:
|
|
case BOX_BR:
|
|
case BOX_TEXT:
|
|
/* should have been wrapped in inline
|
|
container by convert_xml_to_box() */
|
|
assert(0);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "%i\n", child->type);
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
table->columns = col_info.num_columns;
|
|
table->rows = col_info.num_rows;
|
|
free(col_info.spans);
|
|
|
|
if (table->children == 0) {
|
|
struct box *row;
|
|
|
|
LOG(("table->children == 0, creating implied row"));
|
|
|
|
assert(table->style != NULL);
|
|
style = talloc_memdup(c, table->style, sizeof *style);
|
|
if (!style) {
|
|
return false;
|
|
}
|
|
css_cascade(style, &css_blank_style, NULL);
|
|
row_group = box_create(style, table->href,
|
|
table->target, 0, 0, c);
|
|
if (!row_group) {
|
|
talloc_free(style);
|
|
return false;
|
|
}
|
|
row_group->type = BOX_TABLE_ROW_GROUP;
|
|
|
|
style = talloc_memdup(c, row_group->style, sizeof *style);
|
|
if (!style) {
|
|
box_free(row_group);
|
|
return false;
|
|
}
|
|
css_cascade(style, &css_blank_style, NULL);
|
|
row = box_create(style, row_group->href,
|
|
row_group->target, 0, 0, c);
|
|
if (!row) {
|
|
talloc_free(style);
|
|
box_free(row_group);
|
|
return false;
|
|
}
|
|
row->type = BOX_TABLE_ROW;
|
|
|
|
row->parent = row_group;
|
|
row_group->children = row_group->last = row;
|
|
|
|
row_group->parent = table;
|
|
table->children = table->last = row_group;
|
|
|
|
table->rows = 1;
|
|
}
|
|
|
|
box_normalise_table_spans(table);
|
|
if (!table_calculate_column_types(table))
|
|
return false;
|
|
if (table->style->border_collapse == CSS_BORDER_COLLAPSE_COLLAPSE)
|
|
table_collapse_borders(table);
|
|
|
|
LOG(("table %p done", table));
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void box_normalise_table_spans(struct box *table)
|
|
{
|
|
struct box *table_row_group;
|
|
struct box *table_row;
|
|
struct box *table_cell;
|
|
unsigned int last_column;
|
|
unsigned int max_extra = 0;
|
|
bool extra;
|
|
bool force = false;
|
|
unsigned int rows_left = table->rows;
|
|
|
|
/* Scan table filling in table the width and height of table cells for
|
|
cells with colspan = 0 or rowspan = 0. Ignore the colspan and
|
|
rowspan of any cells that that follow an colspan = 0 */
|
|
for (table_row_group = table->children; table_row_group != NULL;
|
|
table_row_group = table_row_group->next) {
|
|
for (table_row = table_row_group->children; NULL != table_row;
|
|
table_row = table_row->next){
|
|
last_column = 0;
|
|
extra = false;
|
|
for (table_cell = table_row->children; NULL != table_cell;
|
|
table_cell = table_cell->next) {
|
|
/* We hae reached the end of the row, and have passed
|
|
a cell with colspan = 0 so ignore col and row spans */
|
|
if (force || extra || (table_cell->start_column + 1 <=
|
|
last_column)) {
|
|
extra = true;
|
|
table_cell->columns = 1;
|
|
table_cell->rows = 1;
|
|
if (table_cell->start_column <= max_extra) {
|
|
max_extra = table_cell->start_column + 1;
|
|
}
|
|
table_cell->start_column += table->columns;
|
|
} else {
|
|
/* Fill out the number of columns or the number of rows
|
|
if necessary */
|
|
if (0 == table_cell->columns) {
|
|
table_cell->columns = table->columns -
|
|
table_cell->start_column;
|
|
if ((0 == table_cell->start_column) &&
|
|
(0 == table_cell->rows)) {
|
|
force = true;
|
|
}
|
|
}
|
|
assert(0 != table_cell->columns);
|
|
if (0 == table_cell->rows) {
|
|
table_cell->rows = rows_left;
|
|
}
|
|
assert(0 != table_cell->rows);
|
|
last_column = table_cell->start_column + 1;
|
|
}
|
|
}
|
|
rows_left--;
|
|
}
|
|
}
|
|
table->columns += max_extra;
|
|
}
|
|
|
|
|
|
bool box_normalise_table_row_group(struct box *row_group,
|
|
struct columns *col_info,
|
|
struct content * c)
|
|
{
|
|
struct box *child;
|
|
struct box *next_child;
|
|
struct box *row;
|
|
struct css_style *style;
|
|
|
|
assert(row_group != 0);
|
|
assert(row_group->type == BOX_TABLE_ROW_GROUP);
|
|
LOG(("row_group %p", row_group));
|
|
|
|
for (child = row_group->children; child != 0; child = next_child) {
|
|
next_child = child->next;
|
|
switch (child->type) {
|
|
case BOX_TABLE_ROW:
|
|
/* ok */
|
|
if (!box_normalise_table_row(child, col_info,
|
|
c))
|
|
return false;
|
|
break;
|
|
case BOX_BLOCK:
|
|
case BOX_INLINE_CONTAINER:
|
|
case BOX_TABLE:
|
|
case BOX_TABLE_ROW_GROUP:
|
|
case BOX_TABLE_CELL:
|
|
/* insert implied table row */
|
|
assert(row_group->style != NULL);
|
|
style = talloc_memdup(c, row_group->style,
|
|
sizeof *style);
|
|
if (!style)
|
|
return false;
|
|
css_cascade(style, &css_blank_style, NULL);
|
|
row = box_create(style, row_group->href,
|
|
row_group->target, 0, 0, c);
|
|
if (!row) {
|
|
talloc_free(style);
|
|
return false;
|
|
}
|
|
row->type = BOX_TABLE_ROW;
|
|
if (child->prev == 0)
|
|
row_group->children = row;
|
|
else
|
|
child->prev->next = row;
|
|
row->prev = child->prev;
|
|
while (child != 0 && (
|
|
child->type == BOX_BLOCK ||
|
|
child->type == BOX_INLINE_CONTAINER ||
|
|
child->type == BOX_TABLE ||
|
|
child->type == BOX_TABLE_ROW_GROUP ||
|
|
child->type == BOX_TABLE_CELL)) {
|
|
box_add_child(row, child);
|
|
next_child = child->next;
|
|
child->next = 0;
|
|
child = next_child;
|
|
}
|
|
assert(row->last != NULL);
|
|
row->last->next = 0;
|
|
row->next = next_child = child;
|
|
if (row->next)
|
|
row->next->prev = row;
|
|
else
|
|
row_group->last = row;
|
|
row->parent = row_group;
|
|
if (!box_normalise_table_row(row, col_info,
|
|
c))
|
|
return false;
|
|
break;
|
|
case BOX_INLINE:
|
|
case BOX_INLINE_END:
|
|
case BOX_INLINE_BLOCK:
|
|
case BOX_FLOAT_LEFT:
|
|
case BOX_FLOAT_RIGHT:
|
|
case BOX_BR:
|
|
case BOX_TEXT:
|
|
/* should have been wrapped in inline
|
|
container by convert_xml_to_box() */
|
|
assert(0);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
if (row_group->children == 0) {
|
|
LOG(("row_group->children == 0, inserting implied row"));
|
|
assert(row_group->style != NULL);
|
|
style = talloc_memdup(c, row_group->style, sizeof *style);
|
|
if (!style) {
|
|
return false;
|
|
}
|
|
css_cascade(style, &css_blank_style, NULL);
|
|
row = box_create(style, row_group->href,
|
|
row_group->target, 0, 0, c);
|
|
if (!row) {
|
|
talloc_free(style);
|
|
return false;
|
|
}
|
|
row->type = BOX_TABLE_ROW;
|
|
|
|
row->parent = row_group;
|
|
row_group->children = row_group->last = row;
|
|
}
|
|
|
|
LOG(("row_group %p done", row_group));
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool box_normalise_table_row(struct box *row,
|
|
struct columns *col_info,
|
|
struct content * c)
|
|
{
|
|
struct box *child;
|
|
struct box *next_child;
|
|
struct box *cell = NULL;
|
|
struct css_style *style;
|
|
unsigned int i;
|
|
|
|
assert(row != 0);
|
|
assert(row->type == BOX_TABLE_ROW);
|
|
LOG(("row %p", row));
|
|
|
|
for (child = row->children; child != 0; child = next_child) {
|
|
next_child = child->next;
|
|
switch (child->type) {
|
|
case BOX_TABLE_CELL:
|
|
/* ok */
|
|
if (!box_normalise_block(child, c))
|
|
return false;
|
|
cell = child;
|
|
break;
|
|
case BOX_BLOCK:
|
|
case BOX_INLINE_CONTAINER:
|
|
case BOX_TABLE:
|
|
case BOX_TABLE_ROW_GROUP:
|
|
case BOX_TABLE_ROW:
|
|
/* insert implied table cell */
|
|
assert(row->style != NULL);
|
|
style = talloc_memdup(c, row->style, sizeof *style);
|
|
if (!style)
|
|
return false;
|
|
css_cascade(style, &css_blank_style, NULL);
|
|
if (child->style && (child->style->position ==
|
|
CSS_POSITION_ABSOLUTE ||
|
|
child->style->position ==
|
|
CSS_POSITION_FIXED)) {
|
|
style->position = child->style->position;
|
|
}
|
|
cell = box_create(style, row->href, row->target, 0, 0,
|
|
c);
|
|
if (!cell) {
|
|
talloc_free(style);
|
|
return false;
|
|
}
|
|
cell->type = BOX_TABLE_CELL;
|
|
if (child->prev == 0)
|
|
row->children = cell;
|
|
else
|
|
child->prev->next = cell;
|
|
cell->prev = child->prev;
|
|
while (child != 0 && (
|
|
child->type == BOX_BLOCK ||
|
|
child->type == BOX_INLINE_CONTAINER ||
|
|
child->type == BOX_TABLE ||
|
|
child->type == BOX_TABLE_ROW_GROUP ||
|
|
child->type == BOX_TABLE_ROW)) {
|
|
box_add_child(cell, child);
|
|
next_child = child->next;
|
|
child->next = 0;
|
|
child = next_child;
|
|
}
|
|
assert(cell->last != NULL);
|
|
cell->last->next = 0;
|
|
cell->next = next_child = child;
|
|
if (cell->next)
|
|
cell->next->prev = cell;
|
|
cell->parent = row;
|
|
if (!box_normalise_block(cell, c))
|
|
return false;
|
|
break;
|
|
case BOX_INLINE:
|
|
case BOX_INLINE_END:
|
|
case BOX_INLINE_BLOCK:
|
|
case BOX_FLOAT_LEFT:
|
|
case BOX_FLOAT_RIGHT:
|
|
case BOX_BR:
|
|
case BOX_TEXT:
|
|
/* should have been wrapped in inline
|
|
container by convert_xml_to_box() */
|
|
assert(0);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
if (!calculate_table_row(col_info, cell->columns, cell->rows,
|
|
&cell->start_column))
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < col_info->num_columns; i++) {
|
|
if ((col_info->spans[i].row_span != 0) && (!col_info->spans[i].auto_row)) {
|
|
col_info->spans[i].row_span--;
|
|
if ((col_info->spans[i].auto_column) && (0 == col_info->spans[i].row_span)) {
|
|
col_info->spans[i].auto_column = false;
|
|
}
|
|
}
|
|
}
|
|
col_info->current_column = 0;
|
|
col_info->extra = false;
|
|
|
|
/* Removing empty rows causes ill effects for HTML such as:
|
|
*
|
|
* <tr><td colspan="2">1</td></tr><tr></tr><tr><td>2</td></tr>
|
|
*
|
|
* as it breaks the colspan value. Additionally, both MSIE and FF
|
|
* render in the same manner as NetSurf does with the empty row
|
|
* culling commented out.
|
|
*/
|
|
// if (row->children == 0) {
|
|
// LOG(("row->children == 0, removing"));
|
|
// if (row->prev == 0)
|
|
// row->parent->children = row->next;
|
|
// else
|
|
// row->prev->next = row->next;
|
|
// if (row->next != 0)
|
|
// row->next->prev = row->prev;
|
|
// box_free(row);
|
|
// } else {
|
|
col_info->num_rows++;
|
|
// }
|
|
|
|
LOG(("row %p done", row));
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* \return true on success, false on memory exhaustion
|
|
*/
|
|
|
|
bool calculate_table_row(struct columns *col_info,
|
|
unsigned int col_span, unsigned int row_span,
|
|
unsigned int *start_column)
|
|
{
|
|
unsigned int cell_start_col;
|
|
unsigned int cell_end_col;
|
|
unsigned int i;
|
|
struct span_info *spans;
|
|
|
|
if (!col_info->extra) {
|
|
/* skip columns with cells spanning from above */
|
|
while ((col_info->spans[col_info->current_column].row_span != 0) &&
|
|
(!col_info->spans[col_info->current_column].auto_column)) {
|
|
col_info->current_column++;
|
|
}
|
|
if (col_info->spans[col_info->current_column].auto_column) {
|
|
col_info->extra = true;
|
|
col_info->current_column = 0;
|
|
}
|
|
}
|
|
|
|
cell_start_col = col_info->current_column;
|
|
|
|
/* If the current table cell follows a cell with colspan=0,
|
|
ignore both colspan and rowspan just assume it is a standard
|
|
size cell */
|
|
if (col_info->extra) {
|
|
col_info->current_column++;
|
|
col_info->extra_columns = col_info->current_column;
|
|
} else {
|
|
/* If span to end of table, assume spaning single column
|
|
at the moment */
|
|
cell_end_col = cell_start_col + ((0 == col_span) ? 1 : col_span);
|
|
|
|
if (col_info->num_columns < cell_end_col) {
|
|
spans = realloc(col_info->spans,
|
|
sizeof *spans * (cell_end_col + 1));
|
|
if (!spans)
|
|
return false;
|
|
col_info->spans = spans;
|
|
col_info->num_columns = cell_end_col;
|
|
|
|
/* Mark new final column as sentinal */
|
|
col_info->spans[cell_end_col].row_span = 0;
|
|
col_info->spans[cell_end_col].auto_row =
|
|
col_info->spans[cell_end_col].auto_column =
|
|
false;
|
|
}
|
|
|
|
if (0 == col_span) {
|
|
col_info->spans[cell_start_col].auto_column = true;
|
|
col_info->spans[cell_start_col].row_span = row_span;
|
|
col_info->spans[cell_start_col].auto_row = (0 == row_span);
|
|
} else {
|
|
for (i = cell_start_col; i < cell_end_col; i++) {
|
|
col_info->spans[i].row_span = (0 == row_span) ?
|
|
1 : row_span;
|
|
col_info->spans[i].auto_row = (0 == row_span);
|
|
col_info->spans[i].auto_column = false;
|
|
}
|
|
}
|
|
if (0 == col_span) {
|
|
col_info->spans[cell_end_col].auto_column = true;
|
|
}
|
|
col_info->current_column = cell_end_col;
|
|
}
|
|
|
|
*start_column = cell_start_col;
|
|
return true;
|
|
}
|
|
|
|
|
|
bool box_normalise_inline_container(struct box *cont, struct content * c)
|
|
{
|
|
struct box *child;
|
|
struct box *next_child;
|
|
|
|
assert(cont != 0);
|
|
assert(cont->type == BOX_INLINE_CONTAINER);
|
|
LOG(("cont %p", cont));
|
|
|
|
for (child = cont->children; child != 0; child = next_child) {
|
|
next_child = child->next;
|
|
switch (child->type) {
|
|
case BOX_INLINE:
|
|
case BOX_INLINE_END:
|
|
case BOX_BR:
|
|
case BOX_TEXT:
|
|
/* ok */
|
|
break;
|
|
case BOX_INLINE_BLOCK:
|
|
/* ok */
|
|
if (!box_normalise_block(child, c))
|
|
return false;
|
|
break;
|
|
case BOX_FLOAT_LEFT:
|
|
case BOX_FLOAT_RIGHT:
|
|
/* ok */
|
|
assert(child->children != 0);
|
|
switch (child->children->type) {
|
|
case BOX_BLOCK:
|
|
if (!box_normalise_block(
|
|
child->children,
|
|
c))
|
|
return false;
|
|
break;
|
|
case BOX_TABLE:
|
|
if (!box_normalise_table(
|
|
child->children,
|
|
c))
|
|
return false;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
if (child->children == 0) {
|
|
/* the child has destroyed itself: remove float */
|
|
if (child->prev == 0)
|
|
child->parent->children = child->next;
|
|
else
|
|
child->prev->next = child->next;
|
|
if (child->next != 0)
|
|
child->next->prev = child->prev;
|
|
box_free(child);
|
|
}
|
|
break;
|
|
case BOX_BLOCK:
|
|
case BOX_INLINE_CONTAINER:
|
|
case BOX_TABLE:
|
|
case BOX_TABLE_ROW_GROUP:
|
|
case BOX_TABLE_ROW:
|
|
case BOX_TABLE_CELL:
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
LOG(("cont %p done", cont));
|
|
|
|
return true;
|
|
}
|