summaryrefslogtreecommitdiff
path: root/render
diff options
context:
space:
mode:
Diffstat (limited to 'render')
-rw-r--r--render/box.c203
-rw-r--r--render/box.h2
-rw-r--r--render/font.h23
-rw-r--r--render/html.c1
-rw-r--r--render/html.h1
-rw-r--r--render/html_redraw.c8
-rw-r--r--render/layout.c92
-rw-r--r--render/loosen.c415
-rw-r--r--render/loosen.h35
-rw-r--r--render/textplain.c10
10 files changed, 735 insertions, 55 deletions
diff --git a/render/box.c b/render/box.c
index 2a29b06c9..e6495174e 100644
--- a/render/box.c
+++ b/render/box.c
@@ -33,7 +33,6 @@
#include "utils/log.h"
#include "utils/talloc.h"
-
static bool box_contains_point(struct box *box, int x, int y);
#define box_is_float(box) (box->type == BOX_FLOAT_LEFT || \
@@ -545,6 +544,10 @@ void box_dump(FILE *stream, struct box *box, unsigned int depth)
fprintf(stream, "(%i %i %i %i) ",
box->descendant_x0, box->descendant_y0,
box->descendant_x1, box->descendant_y1);
+
+ fprintf(stream, "m(%i %i %i %i) ",
+ box->margin[TOP], box->margin[LEFT],
+ box->margin[BOTTOM], box->margin[RIGHT]);
switch (box->type) {
case BOX_BLOCK: fprintf(stream, "BLOCK "); break;
@@ -638,3 +641,201 @@ void box_dump(FILE *stream, struct box *box, unsigned int depth)
box_dump(stream, c, depth + 1);
}
}
+
+/* Box tree duplication below
+*/
+
+/** structure for translating addresses in the box tree */
+struct box_dict_element{
+ struct box *old, *new;
+};
+
+static bool box_duplicate_main_tree(struct box *box, struct content *c,
+ int *count);
+static void box_duplicate_create_dict(struct box *old_box, struct box *new_box,
+ struct box_dict_element **dict);
+static void box_duplicate_update( struct box *box,
+ struct box_dict_element *dict,
+ int n);
+
+static int box_compare_dict_elements(const struct box_dict_element *a,
+ const struct box_dict_element *b);
+
+int box_compare_dict_elements(const struct box_dict_element *a,
+ const struct box_dict_element *b)
+{
+ return (a->old < b->old) ? -1 : ((a->old > b->old) ? 1 : 0);
+}
+
+/** Duplication of a box tree. We assume that all the content is fetched,
+fallbacks have been applied where necessary and we reuse a lot of content
+like strings, fetched objects etc - just replicating all we need to create
+two different layouts.
+\return true on success, false on lack of memory
+*/
+struct box* box_duplicate_tree(struct box *root, struct content *c)
+{
+ struct box *new_root;/**< Root of the new box tree*/
+ int box_number = 0;
+ struct box *old_addr, *new_addr;
+ struct box_dict_element *box_dict, *box_dict_end;
+
+ /* 1. Duplicate parent - children structure, list_markers*/
+ new_root = talloc_memdup(c, root, sizeof (struct box));
+ if (!box_duplicate_main_tree(new_root, c, &box_number))
+ return NULL;
+
+ /* 2. Create address translation dictionary*/
+ /*TODO: dont save unnecessary addresses*/
+
+ box_dict_end = box_dict = malloc(box_number *
+ sizeof(struct box_dict_element));
+
+ if (box_dict == NULL)
+ return NULL;
+ box_duplicate_create_dict(root, new_root, &box_dict_end);
+
+ assert((box_dict_end - box_dict) == box_number);
+
+ /*3. Sort it*/
+
+ qsort(box_dict, (box_dict_end - box_dict), sizeof(struct box_dict_element),
+ (int (*)(const void *, const void *))box_compare_dict_elements);
+
+ /* 4. Update inline_end and float_children pointers */
+
+ box_duplicate_update(new_root, box_dict, (box_dict_end - box_dict));
+
+ free(box_dict);
+
+ return new_root;
+}
+
+/**
+ * Recursively duplicates children of an element, and also if present - its
+ * list_marker, style and text.
+ * \param box Current box to duplicate its children
+ * \param c talloc memory pool
+ * \param count number of boxes seen so far
+ * \return true if successful, false otherwise (lack of memory)
+*/
+bool box_duplicate_main_tree(struct box *box, struct content *c, int *count)
+{
+
+ struct box *b, *prev, *copy;
+
+ prev = NULL;
+
+ for (b = box->children; b; b = b->next) {
+ /*Copy child*/
+ copy = talloc_memdup(c, b, sizeof (struct box));
+ if (copy == NULL)
+ return false;
+
+ copy->parent = box;
+
+ if (prev != NULL)
+ prev->next = copy;
+ else
+ box->children = copy;
+
+ /* Recursively visit child */
+ box_duplicate_main_tree(copy, c, count);
+
+ prev = copy;
+ }
+
+ box->last = prev;
+
+ if (box->list_marker) {
+ box->list_marker = talloc_memdup(c, box->list_marker,
+ sizeof *box->list_marker);
+ if (box->list_marker == NULL)
+ return false;
+ box->list_marker->parent = box;
+ }
+
+ if (box->text) {
+ box->text = talloc_memdup(c, box->text, box->length);
+ if (box->text == NULL)
+ return false;
+ }
+
+ if (box->style) {
+ box->style = talloc_memdup(c, box->style, sizeof *box->style);
+ if (box->style == NULL)
+ return false;
+ }
+
+ /*Make layout calculate the size of this element later
+ (might change because of font change etc.) */
+ box->width = UNKNOWN_WIDTH;
+ box->min_width = 0;
+ box->max_width = UNKNOWN_MAX_WIDTH;
+
+ (*count)++;
+
+ return true;
+}
+
+/**
+ * Recursively creates a dictionary of addresses - binding the address of a box
+ * with its copy.
+ * \param old_box original box
+ * \param new_box copy of the original box
+ * \param dict pointer to a pointer to the current position in the dictionary
+ */
+void box_duplicate_create_dict(struct box *old_box, struct box *new_box,
+ struct box_dict_element **dict)
+{
+ /**Children of the old and new boxes*/
+ struct box *b_old, *b_new;
+
+ for (b_old = old_box->children, b_new = new_box->children;
+ b_old != NULL && b_new != NULL;
+ b_old = b_old->next, b_new = b_new->next)
+ box_duplicate_create_dict(b_old, b_new, dict);
+
+ /*The new tree should be a exact copy*/
+ assert(b_old == NULL && b_new == NULL);
+
+ (*dict)->old = old_box;
+ (*dict)->new = new_box;
+ (*dict)++;
+}
+
+/**
+ * Recursively updates pointers in box tree.
+ * \param box current box in the new box tree
+ * \param box_dict box pointers dictionary
+ * \param n number of memory addresses in the dictionary
+ */
+void box_duplicate_update(struct box *box,
+ struct box_dict_element *box_dict,
+ int n)
+{
+ struct box_dict_element *box_dict_element;
+ struct box *b;
+ struct box_dict_element element;
+
+ for (b = box->children; b; b = b->next)
+ box_duplicate_update(b, box_dict, n);
+
+ if (box->float_children) {
+ element.old = box->float_children;
+ box_dict_element = bsearch(&element,
+ box_dict, n,
+ sizeof(struct box_dict_element),
+ (int (*)(const void *, const void *))box_compare_dict_elements);
+ box->float_children = box_dict_element->new;
+ }
+
+ if (box->next_float) {
+ element.old = box->next_float;
+ box_dict_element = bsearch(&element,
+ box_dict, n,
+ sizeof(struct box_dict_element),
+ (int (*)(const void *, const void *))box_compare_dict_elements);
+ box->next_float = box_dict_element->new;
+ }
+}
diff --git a/render/box.h b/render/box.h
index f4c9cfe2d..a670fddb5 100644
--- a/render/box.h
+++ b/render/box.h
@@ -311,4 +311,6 @@ bool xml_to_box(xmlNode *n, struct content *c);
bool box_normalise_block(struct box *block, struct content *c);
+struct box* box_duplicate_tree(struct box *root, struct content *c);
+
#endif
diff --git a/render/font.h b/render/font.h
index 1bb26e693..277ff810e 100644
--- a/render/font.h
+++ b/render/font.h
@@ -39,14 +39,19 @@
struct css_style;
-bool nsfont_width(const struct css_style *style,
- const char *string, size_t length,
- int *width);
-bool nsfont_position_in_string(const struct css_style *style,
- const char *string, size_t length,
- int x, size_t *char_offset, int *actual_x);
-bool nsfont_split(const struct css_style *style,
- const char *string, size_t length,
- int x, size_t *char_offset, int *actual_x);
+struct font_functions
+{
+ bool (*font_width)(const struct css_style *style,
+ const char *string, size_t length,
+ int *width);
+ bool (*font_position_in_string)(const struct css_style *style,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x);
+ bool (*font_split)(const struct css_style *style,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x);
+};
+
+extern const struct font_functions nsfont;
#endif
diff --git a/render/html.c b/render/html.c
index 59cbde11f..64d3e1386 100644
--- a/render/html.c
+++ b/render/html.c
@@ -122,6 +122,7 @@ bool html_create(struct content *c, const char *params[])
html->page = 0;
html->index = 0;
html->box = 0;
+ html->font_func = &nsfont;
for (i = 0; params[i]; i += 2) {
if (strcasecmp(params[i], "charset") == 0) {
diff --git a/render/html.h b/render/html.h
index af6a886bf..5851b83b8 100644
--- a/render/html.h
+++ b/render/html.h
@@ -130,6 +130,7 @@ struct content_html_data {
struct box *layout; /**< Box tree, or 0. */
colour background_colour; /**< Document background colour. */
+ const struct font_functions *font_func;
/** Number of entries in stylesheet_content. */
unsigned int stylesheet_count;
diff --git a/render/html_redraw.c b/render/html_redraw.c
index ae008f81f..5169faf4a 100644
--- a/render/html_redraw.c
+++ b/render/html_redraw.c
@@ -623,17 +623,17 @@ bool text_redraw(const char *utf8_text, size_t utf8_len,
endtxt_idx = utf8_len;
}
- if (!nsfont_width(style, utf8_text, start_idx, &startx))
+ if (!nsfont.font_width(style, utf8_text, start_idx, &startx))
startx = 0;
- if (!nsfont_width(style, utf8_text, endtxt_idx, &endx))
+ if (!nsfont.font_width(style, utf8_text, endtxt_idx, &endx))
endx = 0;
/* is there a trailing space that should be highlighted as well? */
if (end_idx > utf8_len) {
int spc_width;
/* \todo is there a more elegant/efficient solution? */
- if (nsfont_width(style, " ", 1, &spc_width))
+ if (nsfont.font_width(style, " ", 1, &spc_width))
endx += spc_width;
}
@@ -1175,7 +1175,7 @@ bool html_redraw_file(int x, int y, int width, int height,
text = messages_get("Form_Drop");
length = strlen(text);
- if (!nsfont_width(box->style, text, length, &text_width))
+ if (!nsfont.font_width(box->style, text, length, &text_width))
return false;
text_width *= scale;
if (width < text_width + 8)
diff --git a/render/layout.c b/render/layout.c
index 09eaac6a0..300513f66 100644
--- a/render/layout.c
+++ b/render/layout.c
@@ -55,7 +55,8 @@
#define AUTO INT_MIN
-static void layout_minmax_block(struct box *block);
+static void layout_minmax_block(struct box *block,
+ const struct font_functions *font_func);
static bool layout_block_object(struct box *block);
static void layout_block_find_dimensions(int available_width, struct box *box);
static bool layout_apply_minmax_height(struct box *box);
@@ -71,24 +72,28 @@ static void layout_find_dimensions(int available_width,
static int layout_clear(struct box *fl, css_clear clear);
static void find_sides(struct box *fl, int y0, int y1,
int *x0, int *x1, struct box **left, struct box **right);
-static void layout_minmax_inline_container(struct box *inline_container);
+static void layout_minmax_inline_container(struct box *inline_container,
+ const struct font_functions *font_func);
static int line_height(struct css_style *style);
static bool layout_line(struct box *first, int *width, int *y,
int cx, int cy, struct box *cont, bool indent,
bool has_text_children,
struct content *content, struct box **next_box);
-static struct box *layout_minmax_line(struct box *first, int *min, int *max);
+static struct box *layout_minmax_line(struct box *first, int *min, int *max,
+ const struct font_functions *font_func);
static int layout_text_indent(struct css_style *style, int width);
static bool layout_float(struct box *b, int width, struct content *content);
static void place_float_below(struct box *c, int width, int cx, int y,
struct box *cont);
static bool layout_table(struct box *box, int available_width,
struct content *content);
-static void layout_minmax_table(struct box *table);
+static void layout_minmax_table(struct box *table,
+ const struct font_functions *font_func);
static void layout_move_children(struct box *box, int x, int y);
static void calculate_mbp_width(struct css_style *style, unsigned int side,
int *fixed, float *frac);
-static void layout_lists(struct box *box);
+static void layout_lists(struct box *box,
+ const struct font_functions *font_func);
static void layout_position_relative(struct box *root);
static void layout_compute_relative_offset(struct box *box, int *x, int *y);
static bool layout_position_absolute(struct box *box,
@@ -116,10 +121,11 @@ bool layout_document(struct content *content, int width, int height)
{
bool ret;
struct box *doc = content->data.html.layout;
+ const struct font_functions *font_func = content->data.html.font_func;
assert(content->type == CONTENT_HTML);
- layout_minmax_block(doc);
+ layout_minmax_block(doc, font_func);
layout_block_find_dimensions(width, doc);
doc->x = doc->margin[LEFT] + doc->border[LEFT];
@@ -150,7 +156,7 @@ bool layout_document(struct content *content, int width, int height)
doc->children->margin[BOTTOM]);
}
- layout_lists(doc);
+ layout_lists(doc, font_func);
layout_position_absolute(doc, doc, 0, 0, content);
layout_position_relative(doc);
@@ -516,7 +522,7 @@ bool layout_block_context(struct box *block, struct content *content)
* 0 <= block->min_width <= block->max_width
*/
-void layout_minmax_block(struct box *block)
+void layout_minmax_block(struct box *block, const struct font_functions *font_func)
{
struct box *child;
int min = 0, max = 0;
@@ -545,7 +551,8 @@ void layout_minmax_block(struct box *block)
if (block->object) {
if (block->object->type == CONTENT_HTML) {
- layout_minmax_block(block->object->data.html.layout);
+ layout_minmax_block(block->object->data.html.layout,
+ font_func);
min = block->object->data.html.layout->min_width;
max = block->object->data.html.layout->max_width;
} else {
@@ -556,13 +563,14 @@ void layout_minmax_block(struct box *block)
for (child = block->children; child; child = child->next) {
switch (child->type) {
case BOX_BLOCK:
- layout_minmax_block(child);
+ layout_minmax_block(child, font_func);
break;
case BOX_INLINE_CONTAINER:
- layout_minmax_inline_container(child);
+ layout_minmax_inline_container(child,
+ font_func);
break;
case BOX_TABLE:
- layout_minmax_table(child);
+ layout_minmax_table(child, font_func);
break;
default:
assert(0);
@@ -1139,7 +1147,8 @@ bool layout_inline_container(struct box *inline_container, int width,
* 0 <= inline_container->min_width <= inline_container->max_width
*/
-void layout_minmax_inline_container(struct box *inline_container)
+void layout_minmax_inline_container(struct box *inline_container,
+ const struct font_functions *font_func)
{
struct box *child;
int line_min = 0, line_max = 0;
@@ -1152,7 +1161,9 @@ void layout_minmax_inline_container(struct box *inline_container)
return;
for (child = inline_container->children; child; ) {
- child = layout_minmax_line(child, &line_min, &line_max);
+ child = layout_minmax_line(child,
+ &line_min, &line_max,
+ font_func);
if (min < line_min)
min = line_min;
if (max < line_max)
@@ -1242,6 +1253,8 @@ bool layout_line(struct box *first, int *width, int *y,
struct css_length gadget_size; /* Checkbox / radio buttons */
gadget_size.unit = CSS_UNIT_EM;
gadget_size.value = 1;
+
+ const struct font_functions *font_func = content->data.html.font_func;
LOG(("first %p, first->text '%.*s', width %i, y %i, cx %i, cy %i",
first, (int) first->length, first->text, *width,
@@ -1334,7 +1347,8 @@ bool layout_line(struct box *first, int *width, int *y,
b->width = 0;
if (b->space) {
/** \todo optimize out */
- nsfont_width(b->style, " ", 1, &space_after);
+ font_func->font_width(b->style, " ", 1,
+ &space_after);
} else {
space_after = 0;
}
@@ -1371,7 +1385,8 @@ bool layout_line(struct box *first, int *width, int *y,
data.select.items; o;
o = o->next) {
int opt_width;
- nsfont_width(b->style, o->text,
+ font_func->font_width(b->style,
+ o->text,
strlen(o->text),
&opt_width);
@@ -1381,7 +1396,7 @@ bool layout_line(struct box *first, int *width, int *y,
b->width = opt_maxwidth;
} else {
- nsfont_width(b->style, b->text,
+ font_func->font_width(b->style, b->text,
b->length, &b->width);
}
}
@@ -1389,7 +1404,8 @@ bool layout_line(struct box *first, int *width, int *y,
x += b->width;
if (b->space)
/** \todo optimize out */
- nsfont_width(b->style, " ", 1, &space_after);
+ font_func->font_width(b->style, " ", 1,
+ &space_after);
else
space_after = 0;
@@ -1529,7 +1545,7 @@ bool layout_line(struct box *first, int *width, int *y,
space_after = 0;
if (b->space)
/** \todo handle errors, optimize */
- nsfont_width(b->style, " ", 1,
+ font_func->font_width(b->style, " ", 1,
&space_after);
} else
space_after = 0;
@@ -1654,7 +1670,7 @@ bool layout_line(struct box *first, int *width, int *y,
w = split_box->width;
else
/** \todo handle errors */
- nsfont_width(split_box->style, split_box->text,
+ font_func->font_width(split_box->style, split_box->text,
space, &w);
LOG(("splitting: split_box %p \"%.*s\", space %zu, w %i, "
@@ -1725,7 +1741,7 @@ bool layout_line(struct box *first, int *width, int *y,
/* fit as many words as possible */
assert(space != 0);
/** \todo handle errors */
- nsfont_split(split_box->style,
+ font_func->font_split(split_box->style,
split_box->text, split_box->length,
x1 - x0 - x - space_before, &space, &w);
LOG(("'%.*s' %i %zu %i", (int) split_box->length,
@@ -1827,7 +1843,8 @@ bool layout_line(struct box *first, int *width, int *y,
*/
struct box *layout_minmax_line(struct box *first,
- int *line_min, int *line_max)
+ int *line_min, int *line_max,
+ const struct font_functions *font_func)
{
int min = 0, max = 0, width, height, fixed;
float frac;
@@ -1855,9 +1872,9 @@ struct box *layout_minmax_line(struct box *first,
if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT) {
assert(b->children);
if (b->children->type == BOX_BLOCK)
- layout_minmax_block(b->children);
+ layout_minmax_block(b->children, font_func);
else
- layout_minmax_table(b->children);
+ layout_minmax_table(b->children, font_func);
b->min_width = b->children->min_width;
b->max_width = b->children->max_width;
if (min < b->min_width)
@@ -1867,7 +1884,7 @@ struct box *layout_minmax_line(struct box *first,
}
if (b->type == BOX_INLINE_BLOCK) {
- layout_minmax_block(b);
+ layout_minmax_block(b, font_func);
if (min < b->min_width)
min = b->min_width;
max += b->max_width;
@@ -1890,7 +1907,7 @@ struct box *layout_minmax_line(struct box *first,
if (0 < fixed)
max += fixed;
if (b->next && b->space) {
- nsfont_width(b->style, " ", 1, &width);
+ font_func->font_width(b->style, " ", 1, &width);
max += width;
}
continue;
@@ -1916,7 +1933,8 @@ struct box *layout_minmax_line(struct box *first,
data.select.items; o;
o = o->next) {
int opt_width;
- nsfont_width(b->style, o->text,
+ font_func->font_width(b->style,
+ o->text,
strlen(o->text),
&opt_width);
@@ -1926,13 +1944,13 @@ struct box *layout_minmax_line(struct box *first,
b->width = opt_maxwidth;
} else {
- nsfont_width(b->style, b->text,
+ font_func->font_width(b->style, b->text,
b->length, &b->width);
}
}
max += b->width;
if (b->next && b->space) {
- nsfont_width(b->style, " ", 1, &width);
+ font_func->font_width(b->style, " ", 1, &width);
max += width;
}
@@ -1942,7 +1960,7 @@ struct box *layout_minmax_line(struct box *first,
for (j = i; j != b->length &&
b->text[j] != ' '; j++)
;
- nsfont_width(b->style, b->text + i,
+ font_func->font_width(b->style, b->text + i,
j - i, &width);
if (min < width)
min = width;
@@ -2560,7 +2578,8 @@ bool layout_table(struct box *table, int available_width,
* 0 <= table->min_width <= table->max_width
*/
-void layout_minmax_table(struct box *table)
+void layout_minmax_table(struct box *table,
+ const struct font_functions *font_func)
{
unsigned int i, j;
int border_spacing_h = 0;
@@ -2597,7 +2616,7 @@ void layout_minmax_table(struct box *table)
if (cell->columns != 1)
continue;
- layout_minmax_block(cell);
+ layout_minmax_block(cell, font_func);
i = cell->start_column;
if (col[i].positioned)
@@ -2620,7 +2639,7 @@ void layout_minmax_table(struct box *table)
if (cell->columns == 1)
continue;
- layout_minmax_block(cell);
+ layout_minmax_block(cell, font_func);
i = cell->start_column;
/* find min width so far of spanned columns, and count
@@ -2766,7 +2785,8 @@ void calculate_mbp_width(struct css_style *style, unsigned int side,
* Layout list markers.
*/
-void layout_lists(struct box *box)
+void layout_lists(struct box *box,
+ const struct font_functions *font_func)
{
struct box *child;
struct box *marker;
@@ -2782,7 +2802,7 @@ void layout_lists(struct box *box)
marker->height) / 2;
} else if (marker->text) {
if (marker->width == UNKNOWN_WIDTH)
- nsfont_width(marker->style,
+ font_func->font_width(marker->style,
marker->text,
marker->length,
&marker->width);
@@ -2798,7 +2818,7 @@ void layout_lists(struct box *box)
/* Gap between marker and content */
marker->x -= 4;
}
- layout_lists(child);
+ layout_lists(child, font_func);
}
}
diff --git a/render/loosen.c b/render/loosen.c
new file mode 100644
index 000000000..91c4056ef
--- /dev/null
+++ b/render/loosen.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.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/>.
+ */
+
+#include <stdbool.h>
+#include <assert.h>
+
+#include "content/content.h"
+
+#include "render/box.h"
+#include "render/font.h"
+
+#include "utils/log.h"
+#include "utils/talloc.h"
+
+#define AUTO INT_MIN
+
+
+static bool loosen_text(struct box *text, int width, struct content *content);
+
+static bool loosen_table(struct box *box, int available_width,
+ struct content *content);
+
+static bool loosen_position_static(struct box *box, int width, int cx,
+ struct content *content);
+
+static bool loosen_shrink_object(struct box *box, int width);
+
+static bool loosen_all_first_pass(struct box *box, int width, int cx,
+ struct content *content);
+static bool loosen_all_second_pass(struct box *box, int width, int cx,
+ struct content *content);
+static bool loosen_all_third_pass(struct box *box, int width, int cx,
+ struct content *content);
+
+/**
+ * Main loosing procedure
+ * \param content Reformated content - talloc memory pool for new boxes
+ * \param layout Root of the loosened box tree
+ * \param width Width the content is intended to fit
+ * \param height Height of a single page - to be taken into consideration for \
+ * preventing elements for being cropped at top/bottom edges of pages.
+ * \return true if successful, false otherwise (lack of memory)
+*/
+bool loosen_document_layout(struct content *content, struct box *layout,
+ int width, int height)
+{
+ /* Optional try - if the current layout is not more than xx% too wide,
+ * maybe we scale the content to preserve the original layout?
+ */
+
+ if (!loosen_all_first_pass(layout, width, 0, content))
+ return false;
+ layout->min_width = 0;
+ layout->max_width = UNKNOWN_MAX_WIDTH;
+ content_reformat(content, width, 0);
+
+ /*Check if pass 1 was enough - if re-layouting doesn't give
+ *us the right width, go on to pass 2. And again - if pass 2 was not
+ *enough - go on to pass 3
+ */
+
+ if (content->width > width) {
+ if (!loosen_all_second_pass(layout, width, 0, content))
+ return false;
+ layout->min_width = 0;
+ layout->max_width = UNKNOWN_MAX_WIDTH;
+ content_reformat(content, width, 0);
+ }
+
+ if (content->width > width) {
+ if (!loosen_all_third_pass(layout, width, 0, content))
+ return false;
+ layout->min_width = 0;
+ layout->max_width = UNKNOWN_MAX_WIDTH;
+ content_reformat(content, width, 0);
+ }
+
+ return true;
+}
+
+/** Primarily - break too wide words into pieces.
+ * \param text - the box that contains text to be broken
+ * \param width Width the content is intended to fit
+ * \param content talloc memory pool for new boxes
+ * \return true if successful, false otherwise
+*/
+bool loosen_text(struct box *text, int width, struct content *content)
+{
+ unsigned int offset;
+ int actual_x;
+
+ int *breaks;
+ int break_count;
+
+ int position, i;
+ const struct font_functions *font_func;
+
+ if (content->type == CONTENT_HTML)
+ font_func = content->data.html.font_func;
+ else
+ return false;
+
+ if (text->width <= width) {
+ LOG(("loosen_text called unnecessary?"));
+ /*Still - not an error for this function*/
+ return true;
+ }
+
+ breaks = malloc( sizeof(int) * text->length);
+ if (breaks == NULL)
+ return false;
+
+ break_count = 0;
+ position = 0;
+
+ while (position < text->length) {
+ font_func->font_position_in_string(text->style,
+ text->text + position,
+ text->length - position,
+ width, &offset, &actual_x);
+
+ if (offset < text->length - position) {
+ /*Another break*/
+ LOG(("Current text broken at offset %d",
+ position + offset));
+ breaks[break_count++] = position + offset-1;
+ }
+
+ position += offset;
+ }
+
+ text->text = talloc_realloc(content, text->text, char,
+ text->length + break_count);
+
+ i = text->length-1;
+ text->length = text->length + break_count;
+
+ for (; i>=0; i--) {
+ text->text[i + break_count] = text->text[i];
+ if (i == breaks[break_count - 1]) {
+ break_count--;
+ text->text[i + break_count] = ' ';
+ }
+ }
+
+ free(breaks);
+
+ return true;
+}
+
+/**
+ * Changing table layout and structure to fit the contents width.
+ * In the most extreme case - the table has no influence on the width
+ * (each row is broken into one-cell rows).
+ * \param table - the box that contains table to be broken
+ * \param width Width the content is intended to fit
+ * \param content talloc memory pool for new boxes
+ * \return true if successful, false otherwise
+ */
+bool loosen_table(struct box *table, int width, struct content *content)
+{
+
+ struct box *row_group, *row, *cell, *br, *prev, *inline_container;
+ unsigned int row_sum;
+ bool first_cell_in_row;
+
+ if (table->min_width <= width)
+ return true;
+
+ inline_container = box_create(0, 0, 0, 0, 0, content);
+ inline_container->type = BOX_INLINE_CONTAINER;
+ inline_container->parent = table;
+ inline_container->style = talloc_memdup(content, table->style,
+ sizeof *table->style);
+
+ prev = NULL;
+
+ for (row_group = table->children; row_group;
+ row_group = row_group->next) {
+ for (row = row_group->children; row; row = row->next) {
+
+ for (cell = row->children; cell; cell = cell->next) {
+ cell->type = BOX_INLINE_BLOCK;
+ cell->prev = prev;
+ cell->parent = inline_container;
+ cell->max_width = width;
+ cell->min_width = 0;
+
+ if (prev!=NULL)
+ prev->next = cell;
+ else
+ inline_container->children = cell;
+
+ prev = cell;
+ }
+
+ br = box_create(0, 0, 0, 0, 0, content);
+ br->type = BOX_BR;
+ br->parent = inline_container;
+ br->prev = prev;
+ br->style = talloc_memdup(content, table->style,
+ sizeof *table->style);
+ br->style->clear = CSS_CLEAR_BOTH;
+
+ if (prev != NULL)
+ prev->next = br;
+ else
+ inline_container->children = br;
+
+ prev = br;
+ }
+ }
+ inline_container->last = prev;
+
+ table->type = BOX_BLOCK;
+ table->children = table->last = inline_container;
+ table->col = NULL;
+
+ return true;
+}
+
+
+/**
+ * Change absolute and relative positioned elements into block elements
+ * in case they are positioned to far to the rigth
+ * \param box - the box that should be changed
+ * \param width Width the content is intended to fit
+ * \param cx current x - not yet in use
+ * \param content talloc memory pool for new boxes
+ * \return true if successful, false otherwise
+ */
+bool loosen_position_static(struct box *box, int width, int cx,
+ struct content *content)
+{
+ assert(box->style);
+
+ if (box->style->position == CSS_POSITION_ABSOLUTE) {
+ box->style->position = CSS_POSITION_NOT_SET;
+ }
+
+ return true;
+}
+
+/**
+ * Shrink an object (esp. an image) to fit the page-width
+ * \note Not sure wheter it won't be better for images to be cropped
+ * \param box - the box that should be changed
+ * \param width Width the content is intended to fit
+ * \return true if successful, false otherwise
+*/
+bool loosen_shrink_object(struct box *box, int width)
+{
+ assert(box->object != NULL);
+
+ box->height = AUTO;
+ box->width = width;
+
+ if (box->style) {
+ box->style->width.width = CSS_WIDTH_PERCENT;
+ box->style->width.value.percent = 100;
+ box->style->height.height= CSS_HEIGHT_AUTO;
+ }
+
+ return true;
+}
+
+/**
+ * Pass 1 of loosening - do such obvious changes as: breaking too long words,
+ * moving absolute positioned objects into the visibile scope of width.
+ * \param box - the box that should be changed
+ * \param width Width the content is intended to fit
+ * \param cx current x - not yet in use
+ * \param content talloc memory pool for new boxes
+ * \return true if successful, false otherwise
+*/
+bool loosen_all_first_pass(struct box *box, int width, int cx,
+ struct content *content)
+{
+ struct box* c;
+ int got_width;
+ int x;
+
+ for (c = box->children; c ; c = c->next) {
+ x = cx + c->x;
+ if (c->children != NULL)
+ if (!loosen_all_first_pass(c, width, x, content))
+ return false;
+
+ if (c->style) {
+ if (c->style->position == CSS_POSITION_RELATIVE ||
+ c->style->position == CSS_POSITION_ABSOLUTE )
+ if (!loosen_position_static(c, width, cx, content))
+ return false;
+ if ( c->style->width.width == CSS_WIDTH_LENGTH &&
+ css_len2px(&c->style->width.value.length, c->style) > width)
+ c->style->width.width = CSS_WIDTH_NOT_SET;
+ }
+
+ if (c->object && c->width > width)
+ if (!loosen_shrink_object(c, width))
+ return false;
+
+ switch (c->type) {
+ case BOX_TEXT:
+ if (!loosen_text(c, width, content))
+ return false;
+ break;
+ }
+
+ c->min_width = 0;
+ c->max_width = UNKNOWN_MAX_WIDTH;
+
+ }
+
+ return true;
+}
+
+/**
+ * Pass 2 of loosening - break tables
+ * \param box - the box that should be changed
+ * \param width Width the content is intended to fit
+ * \param cx current x - not yet in use
+ * \param content talloc memory pool for new boxes
+ * \return true if successful, false otherwise
+ */
+bool loosen_all_second_pass(struct box *box, int width, int cx,
+ struct content *content)
+{
+ struct box *c;
+ int got_width;
+ int x;
+
+ for (c = box->children; c; c = c->next) {
+ x = cx + c->x;
+ if (c->children != NULL)
+ if (!loosen_all_second_pass(c, width, x, content))
+ return false;
+
+ switch (c->type) {
+ case BOX_TABLE:
+ if (!loosen_table(c, width, content))
+ return false;
+ break;
+ }
+
+ c->min_width = 0;
+ c->max_width = UNKNOWN_MAX_WIDTH;
+ }
+
+ return true;
+}
+
+
+/**
+ * Pass 3 of loosening -zero all margins and paddings
+ * \param box - the box that should be changed
+ * \param width Width the content is intended to fit
+ * \param cx current x - not yet in use
+ * \param content talloc memory pool for new boxes
+ * \return true if successful, false otherwise
+ */
+bool loosen_all_third_pass(struct box *box, int width, int cx,
+ struct content *content)
+{
+ struct box *c;
+ int got_width;
+ int x;
+
+ for (c = box->children; c; c = c->next) {
+ x = cx + c->x;
+ if (c->children != NULL)
+ if (!loosen_all_third_pass(c, width, x, content))
+ return false;
+
+ c->padding[LEFT] = c->padding[RIGHT] = 0;
+ c->margin[LEFT] = c->margin[RIGHT] = 0;
+
+ if (c->style) {
+ c->style->margin[LEFT].margin = CSS_MARGIN_PERCENT;
+ c->style->margin[LEFT].value.percent = 0;
+
+ c->style->margin[RIGHT].margin = CSS_MARGIN_PERCENT;
+ c->style->margin[RIGHT].value.percent = 0;
+
+ c->style->padding[LEFT].padding = CSS_PADDING_PERCENT;
+ c->style->padding[LEFT].value.percent = 0;
+
+ c->style->padding[RIGHT].padding = CSS_PADDING_PERCENT;
+ c->style->padding[RIGHT].value.percent = 0;
+
+ }
+
+ c->min_width = 0;
+ c->max_width = UNKNOWN_MAX_WIDTH;
+
+ }
+
+ return true;
+}
+
diff --git a/render/loosen.h b/render/loosen.h
new file mode 100644
index 000000000..a5b3822bd
--- /dev/null
+++ b/render/loosen.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.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
+General idea - a set of routines working themselves recursively through
+the box tree and trying to change the layout of the document as little
+as possible to acquire the desired width ( - to make it fit in a printed
+page ), where possible - also taking the dividing height into consideration,
+to prevent objects being cut by ends of pages.
+*/
+
+#ifndef NETSURF_RENDER_LOOSEN_H
+#define NETSURF_RENDER_LOOSEN_H
+#include <stdbool.h>
+
+bool loosen_document_layout(struct content *content, struct box *layout,
+ int width, int height);
+
+#endif
diff --git a/render/textplain.c b/render/textplain.c
index 63610ba17..a16f834c2 100644
--- a/render/textplain.c
+++ b/render/textplain.c
@@ -213,7 +213,7 @@ void textplain_reformat(struct content *c, int width, int height)
/* compute available columns (assuming monospaced font) - use 8
* characters for better accuracy */
- if (!nsfont_width(&textplain_style, "ABCDEFGH", 8, &character_width))
+ if (!nsfont.font_width(&textplain_style, "ABCDEFGH", 8, &character_width))
return;
columns = (width - MARGIN - MARGIN) * 8 / character_width;
textplain_tab_width = (TAB_WIDTH * character_width) / 8;
@@ -402,7 +402,7 @@ bool textplain_redraw(struct content *c, int x, int y,
break;
/* locate end of string and align to next tab position */
- if (nsfont_width(&textplain_style, &text[offset],
+ if (nsfont.font_width(&textplain_style, &text[offset],
next_offset - offset, &width))
tx += (int)(width * scale);
@@ -499,13 +499,13 @@ size_t textplain_offset_from_coords(struct content *c, int x, int y, int dir)
next_offset = utf8_next(text, length, next_offset);
if (next_offset < length)
- nsfont_width(&textplain_style, text, next_offset, &width);
+ nsfont.font_width(&textplain_style, text, next_offset, &width);
if (x <= width) {
int pixel_offset;
size_t char_offset;
- nsfont_position_in_string(&textplain_style,
+ nsfont.font_position_in_string(&textplain_style,
text, next_offset, x,
&char_offset, &pixel_offset);
@@ -584,7 +584,7 @@ int textplain_coord_from_offset(const char *text, size_t offset, size_t length)
while (next_offset < offset && text[next_offset] != '\t')
next_offset = utf8_next(text, length, next_offset);
- nsfont_width(&textplain_style, text, next_offset, &tx);
+ nsfont.font_width(&textplain_style, text, next_offset, &tx);
x += tx;
if (next_offset >= offset)