📄 tables.c
字号:
/* HTML tables renderer */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <stdlib.h>#include <string.h>#include "elinks.h"#include "document/html/parser/parse.h"#include "document/html/parser/table.h"#include "document/html/parser.h"#include "document/html/renderer.h"#include "document/html/tables.h"#include "document/options.h"#include "terminal/draw.h"#include "util/color.h"#include "util/conv.h"#include "util/error.h"#include "util/memory.h"#include "util/string.h"/* Unsafe macros */#include "document/html/internal.h"struct table_frames { unsigned int top:1; unsigned int bottom:1; unsigned int left:1; unsigned int right:1;};static voidget_table_frames(struct table *table, struct table_frames *result){ assert(table && result); if (table->border) { result->top = !!(table->frame & TABLE_FRAME_ABOVE); result->bottom = !!(table->frame & TABLE_FRAME_BELOW); result->left = !!(table->frame & TABLE_FRAME_LHS); result->right = !!(table->frame & TABLE_FRAME_RHS); } else { memset(result, 0, sizeof(*result)); }}/* Distance of the table from the left margin. */static intget_table_indent(struct html_context *html_context, struct table *table){ int width = par_format.width - table->real_width; int indent; switch (table->align) { case ALIGN_CENTER: indent = (width + par_format.leftmargin - par_format.rightmargin) / 2; break; case ALIGN_RIGHT: indent = width - par_format.rightmargin; break; case ALIGN_LEFT: case ALIGN_JUSTIFY: default: indent = par_format.leftmargin; } /* Don't use int_bounds(&x, 0, width) here, * width may be < 0. --Zas */ if (indent > width) indent = width; if (indent < 0) indent = 0; return indent;}static inline struct part *format_cell(struct html_context *html_context, struct table *table, struct table_cell *cell, struct document *document, int x, int y, int width){ if (document) { x += table->part->box.x; y += table->part->box.y; } return format_html_part(html_context, cell->start, cell->end, cell->align, table->cellpadding, width, document, x, y, NULL, cell->link_num);}static inline voidget_cell_width(struct html_context *html_context, unsigned char *start, unsigned char *end, int cellpadding, int width, int a, int *min, int *max, int link_num, int *new_link_num){ struct part *part; if (min) *min = -1; if (max) *max = -1; if (new_link_num) *new_link_num = link_num; part = format_html_part(html_context, start, end, ALIGN_LEFT, cellpadding, width, NULL, !!a, !!a, NULL, link_num); if (!part) return; if (min) *min = part->box.width; if (max) *max = part->max_width; if (new_link_num) *new_link_num = part->link_num; if (min && max) { assertm(*min <= *max, "get_cell_width: %d > %d", *min, *max); } mem_free(part);}static voidget_cell_widths(struct html_context *html_context, struct table *table){ int link_num = table->part->link_num; if (!html_context->options->table_order) { int col, row; for (row = 0; row < table->rows; row++) for (col = 0; col < table->cols; col++) { struct table_cell *cell = CELL(table, col, row); if (!cell->start) continue; cell->link_num = link_num; get_cell_width(html_context, cell->start, cell->end, table->cellpadding, 0, 0, &cell->min_width, &cell->max_width, link_num, &link_num); } } else { int col, row; for (col = 0; col < table->cols; col++) for (row = 0; row < table->rows; row++) { struct table_cell *cell = CELL(table, col, row); if (!cell->start) continue; cell->link_num = link_num; get_cell_width(html_context, cell->start, cell->end, table->cellpadding, 0, 0, &cell->min_width, &cell->max_width, link_num, &link_num); } } table->link_num = link_num;}static inline voiddistribute_values(int *values, int count, int wanted, int *limits){ int i; int sum = 0, d, r, t; for (i = 0; i < count; i++) sum += values[i]; if (sum >= wanted) return;again: t = wanted - sum; d = t / count; r = t % count; wanted = 0; if (limits) { for (i = 0; i < count; i++) { int delta; values[i] += d + (i < r); delta = values[i] - limits[i]; if (delta > 0) { wanted += delta; values[i] = limits[i]; } } } else { for (i = 0; i < count; i++) { values[i] += d + (i < r); } } if (wanted) { assertm(limits, "bug in distribute_values()"); limits = NULL; sum = 0; goto again; }}/* Returns: -1 none, 0, space, 1 line, 2 double */static inline intget_vline_width(struct table *table, int col){ int width = 0; if (!col) return -1; if (table->rules == TABLE_RULE_COLS || table->rules == TABLE_RULE_ALL) width = table->cellspacing; else if (table->rules == TABLE_RULE_GROUPS) width = (col < table->columns_count && table->columns[col].group); if (!width && table->cellpadding) width = -1; return width;}static intget_hline_width(struct table *table, int row){ if (!row) return -1; if (table->rules == TABLE_RULE_ROWS || table->rules == TABLE_RULE_ALL) { if (table->cellspacing || table->vcellpadding) return table->cellspacing; return -1; } else if (table->rules == TABLE_RULE_GROUPS) { int col; for (col = 0; col < table->cols; col++) if (CELL(table, col, row)->is_group) { if (table->cellspacing || table->vcellpadding) return table->cellspacing; return -1; } } return table->vcellpadding ? 0 : -1;}#define has_vline_width(table, col) (get_vline_width(table, col) >= 0)#define has_hline_width(table, row) (get_hline_width(table, row) >= 0)static intget_column_widths(struct table *table){ int colspan; if (!table->cols) return -1; /* prevents calloc(0, ...) calls */ if (!table->min_cols_widths) { table->min_cols_widths = mem_calloc(table->cols, sizeof(*table->min_cols_widths)); if (!table->min_cols_widths) return -1; } if (!table->max_cols_widths) { table->max_cols_widths = mem_calloc(table->cols, sizeof(*table->max_cols_widths)); if (!table->max_cols_widths) { mem_free_set(&table->min_cols_widths, NULL); return -1; } } if (!table->cols_widths) { table->cols_widths = mem_calloc(table->cols, sizeof(*table->cols_widths)); if (!table->cols_widths) { mem_free_set(&table->min_cols_widths, NULL); mem_free_set(&table->max_cols_widths, NULL); return -1; } } colspan = 1; do { int col, row; int new_colspan = INT_MAX; for (col = 0; col < table->cols; col++) for (row = 0; row < table->rows; row++) { struct table_cell *cell = CELL(table, col, row); if (cell->is_spanned || !cell->is_used) continue; assertm(cell->colspan + col <= table->cols, "colspan out of table"); if_assert_failed return -1; if (cell->colspan == colspan) { int k, p = 0; for (k = 1; k < colspan; k++) p += has_vline_width(table, col + k); distribute_values(&table->min_cols_widths[col], colspan, cell->min_width - p, &table->max_cols_widths[col]); distribute_values(&table->max_cols_widths[col], colspan, cell->max_width - p, NULL); for (k = 0; k < colspan; k++) { int tmp = col + k; int_lower_bound(&table->max_cols_widths[tmp], table->min_cols_widths[tmp]); } } else if (cell->colspan > colspan && cell->colspan < new_colspan) { new_colspan = cell->colspan; } } colspan = new_colspan; } while (colspan != INT_MAX); return 0;}static voidget_table_width(struct table *table){ struct table_frames table_frames; int min = 0; int max = 0; int col; for (col = 0; col < table->cols; col++) { int vl = has_vline_width(table, col); min += vl + table->min_cols_widths[col]; max += vl + table->max_cols_widths[col]; if (table->cols_x[col] > table->max_cols_widths[col]) max += table->cols_x[col]; } get_table_frames(table, &table_frames); table->min_width = min + table_frames.left + table_frames.right; table->max_width = max + table_frames.left + table_frames.right; assertm(min <= max, "min(%d) > max(%d)", min, max); /* XXX: Recovery path? --pasky */}/* Initialize @width and @max_width arrays depending on the @stretch_method */static inline intapply_stretch_method(struct table *table, int widths[], int max_widths[], int stretch_method, int max_cols_width){ int col, total_width = 0; for (col = 0; col < table->cols; col++) { switch (stretch_method) { case 0: if (table->cols_widths[col] >= table->cols_x[col]) break; widths[col] = 1; max_widths[col] = int_min(table->cols_x[col], table->max_cols_widths[col]) - table->cols_widths[col]; if (max_widths[col] <= 0) widths[col] = 0; break; case 1: if (table->cols_x[col] > WIDTH_RELATIVE) break; widths[col] = WIDTH_RELATIVE - table->cols_x[col]; max_widths[col] = table->max_cols_widths[col] - table->cols_widths[col]; if (max_widths[col] <= 0) widths[col] = 0; break; case 2: if (table->cols_x[col] != WIDTH_AUTO) break; /* Fall-through */ case 3: if (table->cols_widths[col] >= table->max_cols_widths[col]) break; max_widths[col] = table->max_cols_widths[col] - table->cols_widths[col]; if (max_cols_width) { widths[col] = 5 + table->max_cols_widths[col] * 10 / max_cols_width; } else { widths[col] = 1; } break; case 4: if (table->cols_x[col] < 0) break; widths[col] = 1; max_widths[col] = table->cols_x[col] - table->cols_widths[col]; if (max_widths[col] <= 0) widths[col] = 0; break; case 5: if (table->cols_x[col] >= 0) break; if (table->cols_x[col] <= WIDTH_RELATIVE) { widths[col] = WIDTH_RELATIVE - table->cols_x[col]; } else { widths[col] = 1; } max_widths[col] = INT_MAX; break; case 6: widths[col] = 1; max_widths[col] = INT_MAX; break; default: return -1; } total_width += widths[col]; } return total_width;}/* Stretches the table columns by distributed the @spare_width among them. * Returns how much of @spare_width was actually distributed. */static inline intstretch_columns(struct table *table, int widths[], int max_widths[], int spare_width, int total_width){ int total_spare_width = spare_width; while (spare_width) { int stretch_width = 0; int stretch_col = -1; int col;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -