[project @ 2002-09-18 19:36:28 by bursa]

New table layout algorithm.

svn path=/import/netsurf/; revision=37
This commit is contained in:
James Bursa 2002-09-18 19:36:28 +00:00
parent df864cf4e5
commit 263d627daa
3 changed files with 237 additions and 105 deletions

View File

@ -1,5 +1,5 @@
/**
* $Id: box.c,v 1.14 2002/09/11 21:19:24 bursa Exp $
* $Id: box.c,v 1.15 2002/09/18 19:36:28 bursa Exp $
*/
#include <assert.h>
@ -61,16 +61,18 @@ struct box * box_create(xmlNode * node, box_type type, struct css_style * style,
box->type = type;
box->node = node;
box->style = style;
box->max_width = UNKNOWN_MAX_WIDTH;
box->text = 0;
box->href = href;
box->length = 0;
box->colspan = 1;
box->columns = 1;
box->next = 0;
box->children = 0;
box->last = 0;
box->parent = 0;
box->float_children = 0;
box->next_float = 0;
box->col = 0;
return box;
}
@ -84,7 +86,12 @@ char * tolat1(const xmlChar * s)
while (*s != 0) {
u = sgetu8(s, &chars);
s += chars;
*d = u < 0x100 ? u : '?';
if (u == 0x09 || u == 0x0a || u == 0x0d)
*d = ' ';
else if ((0x20 <= u && u <= 0x7f) || (0xa0 <= u && u <= 0xff))
*d = u;
else
*d = '?';
d++;
}
*d = 0;
@ -231,10 +238,10 @@ struct box * convert_xml_to_box(xmlNode * n, struct css_style * parent_style,
case CSS_DISPLAY_TABLE_CELL:
box = box_create(n, BOX_TABLE_CELL, style, href);
if ((s = (char *) xmlGetProp(n, (xmlChar *) "colspan"))) {
if ((box->colspan = strtol(s, 0, 10)) == 0)
box->colspan = 1;
if ((box->columns = strtol(s, 0, 10)) == 0)
box->columns = 1;
} else
box->colspan = 1;
box->columns = 1;
box_add_child(parent, box);
inline_container_c = 0;
for (c = n->children; c != 0; c = c->next)
@ -322,6 +329,8 @@ void box_dump(struct box * box, unsigned int depth)
fprintf(stderr, " ");
fprintf(stderr, "x%li y%li w%li h%li ", box->x, box->y, box->width, box->height);
if (box->max_width != UNKNOWN_MAX_WIDTH)
fprintf(stderr, "min%lu max%lu ", box->min_width, box->max_width);
switch (box->type) {
case BOX_BLOCK: fprintf(stderr, "BOX_BLOCK "); break;
@ -330,8 +339,8 @@ void box_dump(struct box * box, unsigned int depth)
(int) box->length, box->text); break;
case BOX_TABLE: fprintf(stderr, "BOX_TABLE "); break;
case BOX_TABLE_ROW: fprintf(stderr, "BOX_TABLE_ROW "); break;
case BOX_TABLE_CELL: fprintf(stderr, "BOX_TABLE_CELL [colspan %i] ",
box->colspan); break;
case BOX_TABLE_CELL: fprintf(stderr, "BOX_TABLE_CELL [columns %i] ",
box->columns); break;
case BOX_TABLE_ROW_GROUP: fprintf(stderr, "BOX_TABLE_ROW_GROUP "); break;
case BOX_FLOAT_LEFT: fprintf(stderr, "BOX_FLOAT_LEFT "); break;
case BOX_FLOAT_RIGHT: fprintf(stderr, "BOX_FLOAT_RIGHT "); break;
@ -404,6 +413,7 @@ void box_normalise_block(struct box *block)
}
prev_child->next = 0;
table->next = child;
table->parent = block;
box_normalise_table(table);
child = table;
break;
@ -456,6 +466,7 @@ void box_normalise_table(struct box *table)
}
prev_child->next = 0;
row_group->next = child;
row_group->parent = table;
box_normalise_table_row_group(row_group);
child = row_group;
break;
@ -515,6 +526,7 @@ void box_normalise_table_row_group(struct box *row_group)
}
prev_child->next = 0;
row->next = child;
row->parent = row_group;
box_normalise_table_row(row);
child = row;
break;
@ -538,6 +550,7 @@ void box_normalise_table_row(struct box *row)
struct box *prev_child = 0;
struct box *cell;
struct css_style *style;
unsigned int columns = 0;
assert(row->type == BOX_TABLE_ROW);
@ -546,6 +559,7 @@ void box_normalise_table_row(struct box *row)
case BOX_TABLE_CELL:
/* ok */
box_normalise_block(child);
columns += child->columns;
break;
case BOX_BLOCK:
case BOX_INLINE_CONTAINER:
@ -573,8 +587,10 @@ void box_normalise_table_row(struct box *row)
}
prev_child->next = 0;
cell->next = child;
cell->parent = row;
box_normalise_block(cell);
child = cell;
columns++;
break;
case BOX_INLINE:
case BOX_FLOAT_LEFT:
@ -587,6 +603,8 @@ void box_normalise_table_row(struct box *row)
assert(0);
}
}
if (row->parent->parent->columns < columns)
row->parent->parent->columns = columns;
}

View File

@ -1,10 +1,11 @@
/**
* $Id: box.h,v 1.8 2002/09/08 18:11:56 bursa Exp $
* $Id: box.h,v 1.9 2002/09/18 19:36:28 bursa Exp $
*/
#ifndef _NETSURF_RENDER_BOX_H_
#define _NETSURF_RENDER_BOX_H_
#include <limits.h>
#include "libxml/HTMLparser.h"
#include "netsurf/render/css.h"
@ -19,23 +20,33 @@ typedef enum {
BOX_FLOAT_LEFT, BOX_FLOAT_RIGHT
} box_type;
struct column {
enum { COLUMN_WIDTH_UNKNOWN = 0, COLUMN_WIDTH_FIXED,
COLUMN_WIDTH_AUTO, COLUMN_WIDTH_PERCENT } type;
unsigned long min, max, width;
};
struct box {
box_type type;
xmlNode * node;
struct css_style * style;
unsigned long x, y, width, height;
unsigned long min_width, max_width;
const char * text;
const char * href;
unsigned int length;
unsigned int colspan;
unsigned int columns;
struct box * next;
struct box * children;
struct box * last;
struct box * parent;
struct box * float_children;
struct box * next_float;
struct column *col;
};
#define UNKNOWN_MAX_WIDTH ULONG_MAX
/**
* interface
*/

View File

@ -1,5 +1,5 @@
/**
* $Id: layout.c,v 1.16 2002/09/11 21:18:18 bursa Exp $
* $Id: layout.c,v 1.17 2002/09/18 19:36:28 bursa Exp $
*/
#include <assert.h>
@ -38,6 +38,9 @@ struct box * layout_line(struct box * first, unsigned long width, unsigned long
void place_float_below(struct box * c, unsigned long width, unsigned long y, struct box * cont);
void layout_table(struct box * box, unsigned long width, struct box * cont,
unsigned long cx, unsigned long cy);
void calculate_widths(struct box *box);
void calculate_inline_container_widths(struct box *box);
void calculate_table_widths(struct box *table);
/**
* convert a struct css_length to pixels
@ -140,12 +143,13 @@ void layout_block(struct box * box, unsigned long width, struct box * cont,
}
box->height = layout_block_children(box, box->width, cont, cx, cy);
switch (style->height.height) {
case CSS_HEIGHT_AUTO:
/* use the computed height */
break;
case CSS_HEIGHT_LENGTH:
box->height = len(&style->height.length, box->style);
break;
case CSS_HEIGHT_AUTO:
default:
/* use the computed height */
break;
}
}
@ -468,19 +472,12 @@ void place_float_below(struct box * c, unsigned long width, unsigned long y, str
void layout_table(struct box * table, unsigned long width, struct box * cont,
unsigned long cx, unsigned long cy)
{
unsigned int columns = 0; /* total columns */
unsigned int auto_columns = 0; /* number of columns with auto width */
unsigned int percent_columns = 0; /* no of columns with percent width */
unsigned int columns = table->columns; /* total columns */
unsigned long table_width;
unsigned long percent_width; /* width available for percent columns */
unsigned long used_width = 0; /* width used by fixed or percent columns */
unsigned long auto_width; /* width of each auto column (all equal) */
unsigned long extra_width = 0; /* extra width for each column if table is wider than columns */
unsigned long x;
unsigned long table_height = 0;
unsigned long *xs; /* array of column x positions */
unsigned int i;
unsigned int subcol;
struct box *c;
struct box *row;
struct box *row_group;
@ -493,6 +490,8 @@ void layout_table(struct box * table, unsigned long width, struct box * cont,
table, width, cont, cx, cy);
#endif
calculate_table_widths(table);
/* find table width */
switch (table->style->width.width) {
case CSS_WIDTH_LENGTH:
@ -507,102 +506,46 @@ void layout_table(struct box * table, unsigned long width, struct box * cont,
break;
}
/* fprintf(stderr, "table width %lu\n", table_width); */
fprintf(stderr, "table width %lu, min %lu, max %lu\n", table_width, table->min_width, table->max_width);
/* calculate number of columns and width used by fixed columns */
assert(table->children != 0 && table->children->children != 0);
for (c = table->children->children->children; c != 0; c = c->next) {
assert(c->type == BOX_TABLE_CELL);
assert(c->style != 0);
switch (c->style->width.width) {
case CSS_WIDTH_LENGTH:
used_width += len(&c->style->width.value.length, c->style);
break;
case CSS_WIDTH_AUTO:
auto_columns += c->colspan;
break;
case CSS_WIDTH_PERCENT:
percent_columns += c->colspan;
break;
default:
break;
if (table_width <= table->min_width) {
/* not enough space: minimise column widths */
for (i = 0; i < table->columns; i++) {
table->col[i].width = table->col[i].min;
}
assert(c->colspan != 0);
columns += c->colspan;
}
assert(columns != 0);
table_width = table->min_width;
} else if (table->max_width <= table_width) {
/* more space than maximum width: maximise widths */
for (i = 0; i < table->columns; i++) {
table->col[i].width = table->col[i].max;
}
table_width = table->max_width;
} else {
/* space between min and max: fill it exactly */
float scale = (float) (table_width - table->min_width) /
(float) (table->max_width - table->min_width);
fprintf(stderr, "filling, scale %f\n", scale);
for (i = 0; i < table->columns; i++) {
table->col[i].width = table->col[i].min +
(table->col[i].max - table->col[i].min) * scale;
}
}
if (percent_columns != 0) {
/* percentages are relative to remaining width */
if (used_width < table_width)
/* fast heuristic */
percent_width = (table_width - used_width) *
percent_columns / (percent_columns + auto_columns);
else
/* unless there is none */
percent_width = table_width;
for (c = table->children->children->children; c != 0; c = c->next)
if (c->style->width.width == CSS_WIDTH_PERCENT)
used_width += percent_width * c->style->width.value.percent / 100;
}
/* fprintf(stderr, "columns %u, auto_columns %u\n", columns, auto_columns); */
if (table_width < used_width) table_width = used_width;
if (auto_columns == 0 && table->style->width.width != CSS_WIDTH_AUTO)
extra_width = (table_width - used_width) / columns;
else if (auto_columns != 0)
auto_width = (table_width - used_width) / auto_columns;
/* fprintf(stderr, "used_width %lu, extra_width %lu, auto_width %lu\n",
used_width, extra_width, auto_width);
fprintf(stderr, "columns widths:\n"); */
/* find column widths */
xs = xcalloc(columns + 1, sizeof(*xs));
xs[0] = x = 0;
for (i = 1, c = table->children->children->children, subcol = 1; c != 0; i++) {
switch (c->style->width.width) {
case CSS_WIDTH_LENGTH:
assert(c->colspan != 0);
x += len(&c->style->width.value.length, c->style) / c->colspan + extra_width;
break;
case CSS_WIDTH_PERCENT:
assert(c->colspan != 0);
x += percent_width * c->style->width.value.percent / 100 / c->colspan
+ extra_width;
break;
case CSS_WIDTH_AUTO:
default:
x += auto_width;
break;
}
assert(i < columns + 1);
xs[i] = x;
if (subcol == c->colspan) {
c = c->next;
subcol = 1;
} else
subcol++;
/* fprintf(stderr, "%i\n", x); */
for (i = 0; i < table->columns; i++) {
x += table->col[i].width;
xs[i + 1] = x;
}
/*printf("\n");*/
if (auto_columns == 0 && table->style->width.width == CSS_WIDTH_AUTO)
table_width = used_width;
/* fprintf(stderr, "table width %lu\n", table_width); */
/* position cells */
for (row_group = table->children; row_group != 0; row_group = row_group->next) {
unsigned long row_group_height = 0;
for (row = row_group->children; row != 0; row = row->next) {
unsigned long row_height = 0;
for (i = 0, c = row->children; c != 0; i += c->colspan, c = c->next) {
for (i = 0, c = row->children; c != 0; i += c->columns, c = c->next) {
assert(c->style != 0);
c->width = xs[i + c->colspan] - xs[i];
c->width = xs[i + c->columns] - xs[i];
c->float_children = 0;
c->height = layout_block_children(c, c->width, c, 0, 0);
if (c->style->height.height == CSS_HEIGHT_LENGTH)
@ -630,3 +573,163 @@ void layout_table(struct box * table, unsigned long width, struct box * cont,
table->height = table_height;
}
/**
* find min, max widths required by boxes
*/
void calculate_widths(struct box *box)
{
struct box *child;
unsigned long min = 0, max = 0, width;
assert(box->type == BOX_TABLE_CELL ||
box->type == BOX_BLOCK ||
box->type == BOX_FLOAT_LEFT || box->type == BOX_FLOAT_RIGHT);
/* check if the widths have already been calculated */
if (box->max_width != UNKNOWN_MAX_WIDTH)
return;
for (child = box->children; child != 0; child = child->next) {
switch (child->type) {
case BOX_BLOCK:
case BOX_TABLE:
if (child->style->width.width == CSS_WIDTH_LENGTH) {
width = len(&child->style->width.value.length,
child->style);
if (min < width) min = width;
if (max < width) max = width;
} else {
if (child->type == BOX_TABLE)
calculate_table_widths(child);
else
calculate_widths(child);
if (min < child->min_width) min = child->min_width;
if (max < child->max_width) max = child->max_width;
}
break;
case BOX_INLINE_CONTAINER:
calculate_inline_container_widths(child);
if (min < child->min_width) min = child->min_width;
if (max < child->max_width) max = child->max_width;
break;
default:
break;
}
}
box->min_width = min;
box->max_width = max;
}
void calculate_inline_container_widths(struct box *box)
{
struct box *child;
unsigned long min = 0, max = 0, width;
char *word, *space;
for (child = box->children; child != 0; child = child->next) {
switch (child->type) {
case BOX_INLINE:
/* max = all one line */
width = font_width(child->style, child->text, child->length);
max += width;
/* min = widest word */
for (word = child->text, space = strchr(child->text, ' ');
space != 0;
word = space + 1, space = strchr(word, ' ')) {
width = font_width(child->style, word, space - word);
if (min < width) min = width;
}
width = font_width(child->style, word, strlen(word));
if (min < width) min = width;
break;
case BOX_FLOAT_LEFT:
case BOX_FLOAT_RIGHT:
if (child->style != 0 &&
child->style->width.width == CSS_WIDTH_LENGTH) {
width = len(&child->style->width.value.length,
child->style);
if (min < width) min = width;
if (max < width) max = width;
} else {
calculate_widths(child);
if (min < child->min_width) min = child->min_width;
if (max < child->max_width) max = child->max_width;
}
break;
default:
assert(0);
}
}
box->min_width = min;
box->max_width = max;
}
void calculate_table_widths(struct box *table)
{
unsigned int i;
struct box *row_group, *row, *cell;
unsigned long width, min_width = 0, max_width = 0;
struct column *col = xcalloc(table->columns, sizeof(*col));
#define WIDTH_FIXED ULONG_MAX
assert(table->children != 0 && table->children->children != 0);
for (row_group = table->children; row_group != 0; row_group = row_group->next) {
assert(row_group->type == BOX_TABLE_ROW_GROUP);
for (row = row_group->children; row != 0; row = row->next) {
assert(row->type == BOX_TABLE_ROW);
for (i = 0, cell = row->children; cell != 0;
i += cell->columns, cell = cell->next) {
assert(cell->type == BOX_TABLE_CELL);
assert(cell->style != 0);
if (col[i].type == COLUMN_WIDTH_FIXED)
continue;
/* ignore specified width if colspan > 1 */
if (cell->style->width.width == CSS_WIDTH_LENGTH &&
cell->columns == 1) {
width = len(&cell->style->width.value.length,
cell->style);
col[i].type = COLUMN_WIDTH_FIXED;
col[i].min = col[i].max = col[i].width = width;
} else {
calculate_widths(cell);
if (col[i].min < cell->min_width)
col[i].min = cell->min_width;
if (col[i].max < cell->max_width)
col[i].max = cell->max_width;
if (col[i].type != COLUMN_WIDTH_UNKNOWN)
continue;
if (cell->style->width.width == CSS_WIDTH_PERCENT) {
col[i].type = COLUMN_WIDTH_PERCENT;
col[i].width = cell->style->width.value.percent;
} else if (cell->style->width.width == CSS_WIDTH_AUTO) {
col[i].type = COLUMN_WIDTH_AUTO;
}
}
}
}
}
for (i = 0; i < table->columns; i++) {
min_width += col[i].min;
max_width += col[i].max;
fprintf(stderr, "col %u, min %lu, max %lu\n", i, col[i].min, col[i].max);
}
table->min_width = min_width;
table->max_width = max_width;
table->col = col;
}