⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 renderer.c

📁 一个很有名的浏览器
💻 C
📖 第 1 页 / 共 4 页
字号:
/* HTML renderer *//* $Id: renderer.c,v 1.516.2.12 2005/05/01 22:50:38 jonas Exp $ */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <ctype.h>#include <stdarg.h>#include <string.h>#include "elinks.h"#include "cache/cache.h"#include "config/options.h"#include "document/docdata.h"#include "document/document.h"#include "document/html/frames.h"#include "document/html/parser.h"#include "document/html/parser/parse.h"#include "document/html/renderer.h"#include "document/html/tables.h"#include "document/options.h"#include "document/refresh.h"#include "document/renderer.h"#include "intl/charsets.h"#include "protocol/uri.h"#include "sched/session.h"#include "terminal/color.h"#include "terminal/draw.h"#include "util/color.h"#include "util/conv.h"#include "util/error.h"#include "util/hash.h"#include "util/lists.h"#include "util/memory.h"#include "util/string.h"#include "util/ttime.h"#include "viewer/text/form.h"#include "viewer/text/view.h"#include "viewer/text/vs.h"/* Unsafe macros */#include "document/html/internal.h"/* Types and structs *//* Tags are used for ``id''s or anchors in the document referenced by the * fragment part of the URI. *//* FIXME: This and find_tag() should be part of the general infrastructure * in document/document.*. --pasky */struct tag {	LIST_HEAD(struct tag);	int x, y;	unsigned char name[1]; /* must be last of struct. --Zas */};enum link_state {	LINK_STATE_NONE,	LINK_STATE_NEW,	LINK_STATE_SAME,};struct link_state_info {	unsigned char *link;	unsigned char *target;	unsigned char *image;	struct form_control *form;};struct table_cache_entry_key {	unsigned char *start;	unsigned char *end;	int align;	int margin;	int width;	int x;	int link_num;};struct table_cache_entry {	LIST_HEAD(struct table_cache_entry);	struct table_cache_entry_key key;	struct part part;};/* Max. entries in table cache used for nested tables. */#define MAX_TABLE_CACHE_ENTRIES 16384/* Global variables */struct renderer_context {	int table_cache_entries;	struct hash *table_cache;	int last_link_to_move;	struct tag *last_tag_to_move;	/* All tags between document->tags and this tag (inclusive) should	 * be aligned to the next line break, unless some real content follows	 * the tag. Therefore, this virtual tags list accumulates new tags as	 * they arrive and empties when some real content is written; if a line	 * break is inserted in the meanwhile, the tags follow it (ie. imagine	 * <a name="x"> <p>, then the "x" tag follows the line breaks inserted	 * by the <p> tag). */	struct tag *last_tag_for_newline;	struct link_state_info link_state_info;	int nobreak;	int nosearchable;	int nowrap; /* Activated/deactivated by SP_NOWRAP. */	struct conv_table *convert_table;	/* Used for setting cache info from HTTP-EQUIV meta tags. */	struct cache_entry *cached;	int g_ctrl_num;	int empty_format;	int did_subscript;};static struct renderer_context renderer_context;/* Prototypes */void line_break(struct part *);void put_chars(struct part *, unsigned char *, int);#define X(x_)	(part->box.x + (x_))#define Y(y_)	(part->box.y + (y_))#define SPACES_GRANULARITY	0x7F#define ALIGN_SPACES(x, o, n) mem_align_alloc(x, o, n, unsigned char, SPACES_GRANULARITY)static intrealloc_line(struct document *document, int y, int length){	struct color_pair colors = INIT_COLOR_PAIR(par_format.bgcolor, 0x0);	struct screen_char *pos, *end;	struct line *line;	if (!realloc_lines(document, y))		return -1;	line = &document->data[y];	if (length < line->length)		return 0;	if (!ALIGN_LINE(&line->chars, line->length, length + 1))		return -1;	/* We cannot rely on the aligned allocation to clear the members for us	 * since for line splitting we simply trim the length. Question is if	 * it is better to to clear the line after the splitting or here. */	end = &line->chars[length];	end->data = ' ';	end->attr = 0;	set_term_color(end, &colors, 0, document->options.color_mode);	for (pos = &line->chars[line->length]; pos < end; pos++) {		copy_screen_chars(pos, end, 1);	}	line->length = length + 1;	return 0;}voidexpand_lines(struct part *part, int x, int y, int lines, color_t bgcolor){	int line;	assert(part && part->document);	if_assert_failed return;	if (!use_document_bg_colors(&part->document->options))		return;	par_format.bgcolor = bgcolor;	for (line = 0; line < lines; line++)		realloc_line(part->document, Y(y + line), X(x));}static inline intrealloc_spaces(struct part *part, int length){	if (length < part->spaces_len)		return 0;	if (!ALIGN_SPACES(&part->spaces, part->spaces_len, length))		return -1;	part->spaces_len = length;	return 0;}#define LINE(y_)	part->document->data[Y(y_)]#define POS(x_, y_)	LINE(y_).chars[X(x_)]#define LEN(y_)		int_max(LINE(y_).length - part->box.x, 0)/* When we clear chars we want to preserve and use the background colors * already in place else we could end up ``staining'' the background especial * when drawing table cells. So make the cleared chars share the colors in * place. */static inline voidclear_hchars(struct part *part, int x, int y, int width){	struct color_pair colors = INIT_COLOR_PAIR(par_format.bgcolor, 0x0);	struct screen_char *pos, *end;	assert(part && part->document && width > 0);	if_assert_failed return;	if (realloc_line(part->document, Y(y), X(x) + width - 1))		return;	assert(part->document->data);	if_assert_failed return;	pos = &POS(x, y);	end = pos + width - 1;	end->data = ' ';	end->attr = 0;	set_term_color(end, &colors, 0, part->document->options.color_mode);	while (pos < end)		copy_screen_chars(pos++, end, 1);}/* TODO: Merge parts with get_format_screen_char(). --jonas *//* Allocates the required chars on the given line and returns the char at * position (x, y) ready to be used as a template char.  */static inline struct screen_char *get_frame_char(struct part *part, int x, int y, unsigned char data,	       color_t bgcolor, color_t fgcolor){	struct color_pair colors = INIT_COLOR_PAIR(bgcolor, fgcolor);	struct screen_char *template;	static enum color_flags color_flags;	static enum color_mode color_mode;	assert(part && part->document && x >= 0 && y >= 0);	if_assert_failed return NULL;	if (realloc_line(part->document, Y(y), X(x)))		return NULL;	assert(part->document->data);	if_assert_failed return NULL;	template = &POS(x, y);	template->data = data;	template->attr = SCREEN_ATTR_FRAME;	color_mode = part->document->options.color_mode;	color_flags = part->document->options.color_flags;	set_term_color(template, &colors, color_flags, color_mode);	return template;}voiddraw_frame_hchars(struct part *part, int x, int y, int width,		  unsigned char data, color_t bgcolor, color_t fgcolor){	struct screen_char *template;	assert(width > 0);	if_assert_failed return;	template = get_frame_char(part, x + width - 1, y, data, bgcolor, fgcolor);	if (!template) return;	/* The template char is the last we need to draw so only decrease @width. */	for (width -= 1; width; width--, x++) {		copy_screen_chars(&POS(x, y), template, 1);	}}voiddraw_frame_vchars(struct part *part, int x, int y, int height,		  unsigned char data, color_t bgcolor, color_t fgcolor){	struct screen_char *template = get_frame_char(part, x, y, data,						      bgcolor, fgcolor);	if (!template) return;	/* The template char is the first vertical char to be drawn. So	 * copy it to the rest. */	for (height -= 1, y += 1; height; height--, y++) {	    	if (realloc_line(part->document, Y(y), X(x)))			return;		copy_screen_chars(&POS(x, y), template, 1);	}}static inline struct screen_char *get_format_screen_char(struct part *part, enum link_state link_state){	static struct text_attrib_style ta_cache = { -1, 0x0, 0x0 };	static struct screen_char schar_cache;	if (memcmp(&ta_cache, &format.style, sizeof(ta_cache))) {		struct color_pair colors = INIT_COLOR_PAIR(format.style.bg, format.style.fg);		static enum color_mode color_mode;		static enum color_flags color_flags;		if (global_doc_opts) {			color_mode = global_doc_opts->color_mode;			color_flags = global_doc_opts->color_flags;		}		schar_cache.attr = 0;		if (format.style.attr) {			if (format.style.attr & AT_UNDERLINE) {				schar_cache.attr |= SCREEN_ATTR_UNDERLINE;			}			if (format.style.attr & AT_BOLD) {				schar_cache.attr |= SCREEN_ATTR_BOLD;			}			if (format.style.attr & AT_ITALIC) {				schar_cache.attr |= SCREEN_ATTR_ITALIC;			}			if (format.style.attr & AT_GRAPHICS) {				schar_cache.attr |= SCREEN_ATTR_FRAME;			}		}		if (link_state != LINK_STATE_NONE		    && global_doc_opts->underline_links) {			schar_cache.attr |= SCREEN_ATTR_UNDERLINE;		}		copy_struct(&ta_cache, &format.style);		set_term_color(&schar_cache, &colors, color_flags, color_mode);		if (global_doc_opts->display_subs) {			if (format.style.attr & AT_SUBSCRIPT) {				if (!renderer_context.did_subscript) {					renderer_context.did_subscript = 1;					put_chars(part, "[", 1);				}			} else {				if (renderer_context.did_subscript) {					put_chars(part, "]", 1);					renderer_context.did_subscript = 0;				}			}		}		if (global_doc_opts->display_sups) {			static int super = 0;			if (format.style.attr & AT_SUPERSCRIPT) {				if (!super) {					super = 1;					put_chars(part, "^", 1);				}			} else {				if (super) {					super = 0;				}			}		}	}	if (!!(schar_cache.attr & SCREEN_ATTR_UNSEARCHABLE)	    ^ !!renderer_context.nosearchable) {		schar_cache.attr ^= SCREEN_ATTR_UNSEARCHABLE;	}	return &schar_cache;}/* First possibly do the format change and then find out what coordinates * to use since sub- or superscript might change them */static inline voidset_hline(struct part *part, unsigned char *chars, int charslen,	  enum link_state link_state){	struct screen_char *schar = get_format_screen_char(part, link_state);	int x = part->cx;	int y = part->cy;	assert(part);	if_assert_failed return;	if (realloc_spaces(part, x + charslen))		return;	if (part->document) {		if (realloc_line(part->document, Y(y), X(x) + charslen - 1))			return;		for (; charslen > 0; charslen--, x++, chars++) {			if (*chars == NBSP_CHAR) {				schar->data = ' ';				part->spaces[x] = global_doc_opts->wrap_nbsp;			} else {				part->spaces[x] = (*chars == ' ');				schar->data = *chars;			}			copy_screen_chars(&POS(x, y), schar, 1);		}	} else {		for (; charslen > 0; charslen--, x++, chars++) {			part->spaces[x] = (*chars == ' ');		}	}}static voidmove_links(struct part *part, int xf, int yf, int xt, int yt){	struct tag *tag;	int nlink = renderer_context.last_link_to_move;	int matched = 0;	assert(part && part->document);	if_assert_failed return;	if (!realloc_lines(part->document, Y(yt)))		return;	for (; nlink < part->document->nlinks; nlink++) {		struct link *link = &part->document->links[nlink];		int i;		for (i = 0; i < link->npoints; i++) {			/* Fix for bug 479 (part one) */			/* The scenario that triggered it:			 *			 * Imagine a centered element containing a really long			 * word (over half of the screen width long) followed			 * by a few links with no spaces between them where all			 * the link text combined with the really long word			 * will force the line to be wrapped. When rendering			 * the line first words (including link text words) are			 * put on one line. Then wrapping is performed moving			 * all links from current line to the one below. Then			 * the current line (now only containing the really			 * long word) is centered. This will trigger a call to			 * move_links() which will increment.			 *			 * Without the fix below the centering of the current			 * line will increment last_link_to_move to that of the			 * last link which means centering of the next line			 * with all the links will only move the last link			 * leaving all the other links' points dangling and			 * causing buggy link highlighting.			 *			 * Even links like textareas will be correctly handled			 * because @last_link_to_move is a way to optimize how			 * many links move_links() will have to iterate and			 * this little fix will only decrease the effect of the			 * optimization by always ensuring it is never			 * incremented too far. */			if (!matched && link->points[i].y > Y(yf)) {				matched = 1;				continue;			}			if (link->points[i].y != Y(yf))

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -