From 415254c326ea4c34d991104ffb2419f57098a7f7 Mon Sep 17 00:00:00 2001 From: James Bursa Date: Sun, 12 Feb 2006 23:07:28 +0000 Subject: [project @ 2006-02-12 23:07:28 by bursa] Rewrite handling of text/plain contents. Now rendered directly instead of converting to HTML. svn path=/import/netsurf/; revision=2078 --- content/content.c | 3 +- content/content.h | 2 + render/textplain.c | 323 +++++++++++++++++++++++++++++++++++++++++++++-------- render/textplain.h | 26 ++++- 4 files changed, 303 insertions(+), 51 deletions(-) diff --git a/content/content.c b/content/content.c index adf0cb6c8..dd0f855d6 100644 --- a/content/content.c +++ b/content/content.c @@ -190,7 +190,8 @@ static const struct handler_entry handler_map[] = { html_open, html_close, true}, {textplain_create, textplain_process_data, textplain_convert, - 0, 0, 0, 0, 0, 0, true}, + textplain_reformat, textplain_destroy, 0, textplain_redraw, + 0, 0, true}, {0, 0, css_convert, 0, css_destroy, 0, 0, 0, 0, false}, #ifdef WITH_JPEG {0, 0, nsjpeg_convert, diff --git a/content/content.h b/content/content.h index 7dbcdcb08..aa963f811 100644 --- a/content/content.h +++ b/content/content.h @@ -108,6 +108,7 @@ #include "netsurf/content/content_type.h" #include "netsurf/css/css.h" #include "netsurf/render/html.h" +#include "netsurf/render/textplain.h" #ifdef WITH_JPEG #include "netsurf/image/jpeg.h" #endif @@ -212,6 +213,7 @@ struct content { /** Data dependent on type. */ union { struct content_html_data html; + struct content_textplain_data textplain; struct content_css_data css; #ifdef WITH_JPEG struct content_jpeg_data jpeg; diff --git a/render/textplain.c b/render/textplain.c index 4b08e0acc..1f455f3ad 100644 --- a/render/textplain.c +++ b/render/textplain.c @@ -2,80 +2,309 @@ * This file is part of NetSurf, http://netsurf.sourceforge.net/ * Licensed under the GNU General Public License, * http://www.opensource.org/licenses/gpl-license - * Copyright 2004 James Bursa + * Copyright 2006 James Bursa */ -#include "libxml/HTMLparser.h" +/** \file + * Content for text/plain (implementation). + */ + +#include +#include #include "netsurf/content/content.h" -#include "netsurf/render/html.h" +#include "netsurf/css/css.h" +#include "netsurf/desktop/gui.h" +#include "netsurf/desktop/plotters.h" +#include "netsurf/render/font.h" #include "netsurf/render/textplain.h" +#include "netsurf/utils/log.h" #include "netsurf/utils/messages.h" +#include "netsurf/utils/talloc.h" +#include "netsurf/utils/utils.h" + +#define CHUNK 20480 +#define MARGIN 4 -static const char header[] = "
";
-static const char footer[] = "
"; +static struct css_style textplain_style; +/** + * Create a CONTENT_TEXTPLAIN. + */ + bool textplain_create(struct content *c, const char *params[]) { - if (!html_create(c, params)) - /* html_create() must have broadcast MSG_ERROR already, so we - * don't need to. */ + unsigned int i; + char *utf8_data; + const char *encoding = "iso-8859-1"; + iconv_t iconv_cd; + union content_msg_data msg_data; + + textplain_style = css_base_style; + textplain_style.font_family = CSS_FONT_FAMILY_MONOSPACE; + + utf8_data = talloc_array(c, char, CHUNK); + if (!utf8_data) + goto no_memory; + + for (i = 0; params[i]; i += 2) { + if (strcasecmp(params[i], "charset") == 0) { + encoding = talloc_strdup(c, params[i + 1]); + if (!encoding) + goto no_memory; + break; + } + } + + iconv_cd = iconv_open("utf-8", encoding); + if (iconv_cd == (iconv_t)(-1) && errno == EINVAL) { + LOG(("unsupported encoding \"%s\"", encoding)); + iconv_cd = iconv_open("utf-8", "iso-8859-1"); + } + if (iconv_cd == (iconv_t)(-1)) { + msg_data.error = strerror(errno); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + warn_user("IconvFailed", strerror(errno)); return false; - htmlParseChunk(c->data.html.parser, header, sizeof(header) - 1, 0); + } + + c->data.textplain.encoding = encoding; + c->data.textplain.iconv_cd = iconv_cd; + c->data.textplain.converted = 0; + c->data.textplain.utf8_data = utf8_data; + c->data.textplain.utf8_data_size = 0; + c->data.textplain.utf8_data_allocated = CHUNK; + c->data.textplain.physical_line_start = 0; + c->data.textplain.physical_line_count = 0; + return true; + +no_memory: + msg_data.error = messages_get("NoMemory"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + warn_user("NoMemory", 0); + return false; } -bool textplain_process_data(struct content *c, char *data, - unsigned int size) + +/** + * Process data for CONTENT_TEXTPLAIN. + */ + +bool textplain_process_data(struct content *c, char *data, unsigned int size) { - unsigned int i, s; - char *d, *p; + iconv_t iconv_cd = c->data.textplain.iconv_cd; + size_t count; union content_msg_data msg_data; - bool ret; - /* count number of '<' in data buffer */ - for (d = data, i = 0, s = 0; i != size; i++, d++) { - if (*d == '<') - s++; - } + do { + char *inbuf = c->source_data + c->data.textplain.converted; + size_t inbytesleft = c->source_size - + c->data.textplain.converted; + char *outbuf = c->data.textplain.utf8_data + + c->data.textplain.utf8_data_size; + size_t outbytesleft = c->data.textplain.utf8_data_allocated - + c->data.textplain.utf8_data_size; + count = iconv(iconv_cd, &inbuf, &inbytesleft, + &outbuf, &outbytesleft); + c->data.textplain.converted = inbuf - c->source_data; + c->data.textplain.utf8_data_size = c->data.textplain. + utf8_data_allocated - outbytesleft; - /* create buffer for modified input */ - d = calloc(size + 3*s, sizeof(char)); - if (!d) { - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; + if (count == (size_t)(-1) && errno == E2BIG) { + size_t allocated = CHUNK + + c->data.textplain.utf8_data_allocated; + char *utf8_data = talloc_realloc(c, + c->data.textplain.utf8_data, + char, allocated); + if (!utf8_data) + goto no_memory; + c->data.textplain.utf8_data = utf8_data; + c->data.textplain.utf8_data_allocated = allocated; + } else if (count == (size_t)(-1) && errno != EINVAL) { + msg_data.error = strerror(errno); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + warn_user("IconvFailed", strerror(errno)); + return false; + } + + gui_multitask(); + } while (!(c->data.textplain.converted == c->source_size || + (count == (size_t)(-1) && errno == EINVAL))); + + return true; + +no_memory: + msg_data.error = messages_get("NoMemory"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + warn_user("NoMemory", 0); + return false; +} + + +/** + * Convert a CONTENT_TEXTPLAIN for display. + */ + +bool textplain_convert(struct content *c, int width, int height) +{ + iconv_close(c->data.textplain.iconv_cd); + c->data.textplain.iconv_cd = 0; + + textplain_reformat(c, width, height); + c->status = CONTENT_STATUS_DONE; + + return true; +} + + +/** + * Reformat a CONTENT_TEXTPLAIN to a new width. + */ + +void textplain_reformat(struct content *c, int width, int height) +{ + char *utf8_data = c->data.textplain.utf8_data; + size_t utf8_data_size = c->data.textplain.utf8_data_size; + unsigned long line_count = 0; + size_t *line_start = c->data.textplain.physical_line_start; + size_t *line_start1; + size_t i, space, col; + size_t columns = 80; + int character_width; + + /* compute available columns (assuming monospaced font) - use 8 + * characters for better accuracy */ + if (!nsfont_width(&textplain_style, "ABCDEFGH", 8, &character_width)) + return; + columns = (width - MARGIN - MARGIN) * 8 / character_width; + + c->data.textplain.physical_line_count = 0; + + if (!line_start) { + c->data.textplain.physical_line_start = line_start = + talloc_array(c, size_t, 1024 + 3); + if (!line_start) + goto no_memory; } - /* copy data across to modified buffer, - * replacing occurrences of '<' with '<' - * This prevents the parser stripping sequences of '<...>' - */ - for (p = d, i = 0, s = 0; i != size; i++, data++) { - if (*data == '<') { - *p++ = '&'; - *p++ = 'l'; - *p++ = 't'; - *p++ = ';'; - s += 4; - } - else { - *p++ = *data; - s++; + line_start[line_count++] = 0; + space = 0; + for (i = 0, col = 0; i != utf8_data_size; i++) { + if (utf8_data[i] == '\n' || col + 1 == columns) { + if (line_count % 1024 == 0) { + line_start1 = talloc_realloc(c, line_start, + size_t, line_count + 1024 + 3); + if (!line_start1) + goto no_memory; + c->data.textplain.physical_line_start = + line_start = line_start1; + } + if (utf8_data[i] != '\n' && space) + i = space; + line_start[line_count++] = i + 1; + col = 0; + space = 0; + } else { + col++; + if (utf8_data[i] == ' ') + space = i; } } + line_start[line_count] = utf8_data_size; - ret = html_process_data(c, d, s); + c->data.textplain.physical_line_count = line_count; + c->width = width; + c->height = line_count * + css_len2px(&textplain_style.font_size.value.length, + &textplain_style) * 1.2 + MARGIN + MARGIN; - free(d); + return; - return ret; +no_memory: + LOG(("out of memory (line_count %lu)", line_count)); + warn_user("NoMemory", 0); + return; } -bool textplain_convert(struct content *c, int width, int height) + +/** + * Destroy a CONTENT_TEXTPLAIN and free all resources it owns. + */ + +void textplain_destroy(struct content *c) +{ + if (c->data.textplain.iconv_cd) + iconv_close(c->data.textplain.iconv_cd); +} + + +/** + * Draw a CONTENT_TEXTPLAIN using the current set of plotters (plot). + * + * \param c content of type CONTENT_TEXTPLAIN + * \param x coordinate for top-left of redraw + * \param y coordinate for top-left of redraw + * \param width available width + * \param height available height + * \param clip_x0 clip rectangle + * \param clip_y0 clip rectangle + * \param clip_x1 clip rectangle + * \param clip_y1 clip rectangle + * \param scale scale for redraw + * \param background_colour the background colour + * \return true if successful, false otherwise + * + * x, y, clip_[xy][01] are in target coordinates. + */ + +bool textplain_redraw(struct content *c, int x, int y, + int width, int height, + int clip_x0, int clip_y0, int clip_x1, int clip_y1, + float scale, unsigned long background_colour) { - htmlParseChunk(c->data.html.parser, footer, sizeof(footer) - 1, 0); - c->type = CONTENT_HTML; - return html_convert(c, width, height); + char *utf8_data = c->data.textplain.utf8_data; + long line; + unsigned long line_count = c->data.textplain.physical_line_count; + float line_height = css_len2px(&textplain_style.font_size.value.length, + &textplain_style) * 1.2 * scale; + long line0 = clip_y0 / line_height - 1; + long line1 = clip_y1 / line_height + 1; + size_t *line_start = c->data.textplain.physical_line_start; + size_t length; + + if (line0 < 0) + line0 = 0; + if (line1 < 0) + line1 = 0; + if (line_count < (unsigned long) line0) + line0 = line_count; + if (line_count < (unsigned long) line1) + line1 = line_count; + if (line1 < line0) + line1 = line0; + + if (!plot.clg(0xffffff)) + return false; + + if (!line_start) + return true; + + x += MARGIN * scale; + y += MARGIN * scale; + for (line = line0; line != line1; line++) { + length = line_start[line + 1] - line_start[line]; + if (!length) + continue; + if (utf8_data[line_start[line] + length - 1] == '\n') + length--; + if (!plot.text(x, y + (line + 1) * line_height, + &textplain_style, + utf8_data + line_start[line], length, + 0xffffff, 0x000000)) + return false; + } + + return true; } diff --git a/render/textplain.h b/render/textplain.h index 74d029932..c60ddadd0 100644 --- a/render/textplain.h +++ b/render/textplain.h @@ -2,7 +2,11 @@ * This file is part of NetSurf, http://netsurf.sourceforge.net/ * Licensed under the GNU General Public License, * http://www.opensource.org/licenses/gpl-license - * Copyright 2004 James Bursa + * Copyright 2006 James Bursa + */ + +/** \file + * Content for text/plain (interface). */ #ifndef _NETSURF_RENDER_TEXTPLAIN_H_ @@ -10,9 +14,25 @@ struct content; +struct content_textplain_data { + const char *encoding; + iconv_t iconv_cd; + size_t converted; + char *utf8_data; + size_t utf8_data_size; + size_t utf8_data_allocated; + unsigned long physical_line_count; + size_t *physical_line_start; +}; + bool textplain_create(struct content *c, const char *params[]); -bool textplain_process_data(struct content *c, char *data, - unsigned int size); +bool textplain_process_data(struct content *c, char *data, unsigned int size); bool textplain_convert(struct content *c, int width, int height); +void textplain_reformat(struct content *c, int width, int height); +void textplain_destroy(struct content *c); +bool textplain_redraw(struct content *c, int x, int y, + int width, int height, + int clip_x0, int clip_y0, int clip_x1, int clip_y1, + float scale, unsigned long background_colour); #endif -- cgit v1.2.3