📄 renderer.c
字号:
/* 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 + -