mirror of
https://github.com/netsurf-browser/netsurf
synced 2025-01-22 02:12:10 +03:00
[project @ 2002-09-18 19:36:28 by bursa]
New table layout algorithm. svn path=/import/netsurf/; revision=37
This commit is contained in:
parent
df864cf4e5
commit
263d627daa
34
render/box.c
34
render/box.c
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
15
render/box.h
15
render/box.h
@ -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
|
||||
*/
|
||||
|
293
render/layout.c
293
render/layout.c
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user