iconv: Bail out of the loop when an illegal sequence of bytes occurs.
[elinks/elinks-j605.git] / src / document / html / tables.c
blobcc4b9c89ef39f447c92a4b39a4af82c272a3a2c9
1 /* HTML tables renderer */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdlib.h>
8 #include <string.h>
10 #include "elinks.h"
12 #include "document/html/parser/parse.h"
13 #include "document/html/parser/table.h"
14 #include "document/html/parser.h"
15 #include "document/html/renderer.h"
16 #include "document/html/tables.h"
17 #include "document/options.h"
18 #include "terminal/draw.h"
19 #include "util/color.h"
20 #include "util/conv.h"
21 #include "util/error.h"
22 #include "util/memory.h"
23 #include "util/string.h"
25 /* Unsafe macros */
26 #include "document/html/internal.h"
29 struct table_frames {
30 unsigned int top:1;
31 unsigned int bottom:1;
32 unsigned int left:1;
33 unsigned int right:1;
36 static void
37 get_table_frames(struct table *table, struct table_frames *result)
39 assert(table && result);
41 if (table->border) {
42 result->top = !!(table->frame & TABLE_FRAME_ABOVE);
43 result->bottom = !!(table->frame & TABLE_FRAME_BELOW);
44 result->left = !!(table->frame & TABLE_FRAME_LHS);
45 result->right = !!(table->frame & TABLE_FRAME_RHS);
46 } else {
47 memset(result, 0, sizeof(*result));
51 /* Distance of the table from the left margin. */
52 static int
53 get_table_indent(struct html_context *html_context, struct table *table)
55 int width = par_format.width - table->real_width;
56 int indent;
58 switch (table->align) {
59 case ALIGN_CENTER:
60 indent = (width + par_format.leftmargin - par_format.rightmargin) / 2;
61 break;
63 case ALIGN_RIGHT:
64 indent = width - par_format.rightmargin;
65 break;
67 case ALIGN_LEFT:
68 case ALIGN_JUSTIFY:
69 default:
70 indent = par_format.leftmargin;
73 /* Don't use int_bounds(&x, 0, width) here,
74 * width may be < 0. --Zas */
75 if (indent > width) indent = width;
76 if (indent < 0) indent = 0;
78 return indent;
81 static inline struct part *
82 format_cell(struct html_context *html_context, struct table *table,
83 struct table_cell *cell, struct document *document,
84 int x, int y, int width)
86 if (document) {
87 x += table->part->box.x;
88 y += table->part->box.y;
91 return format_html_part(html_context, cell->start, cell->end,
92 cell->align, table->cellpadding, width,
93 document, x, y, NULL, cell->link_num);
96 static inline void
97 get_cell_width(struct html_context *html_context,
98 unsigned char *start, unsigned char *end,
99 int cellpadding, int width,
100 int a, int *min, int *max,
101 int link_num, int *new_link_num)
103 struct part *part;
105 if (min) *min = -1;
106 if (max) *max = -1;
107 if (new_link_num) *new_link_num = link_num;
109 part = format_html_part(html_context, start, end, ALIGN_LEFT,
110 cellpadding, width, NULL,
111 !!a, !!a, NULL, link_num);
112 if (!part) return;
114 if (min) *min = part->box.width;
115 if (max) *max = part->max_width;
116 if (new_link_num) *new_link_num = part->link_num;
118 if (min && max) {
119 assertm(*min <= *max, "get_cell_width: %d > %d", *min, *max);
122 mem_free(part);
125 static void
126 get_cell_widths(struct html_context *html_context, struct table *table)
128 int link_num = table->part->link_num;
130 if (!html_context->options->table_order) {
131 int col, row;
133 for (row = 0; row < table->rows; row++)
134 for (col = 0; col < table->cols; col++) {
135 struct table_cell *cell = CELL(table, col, row);
137 if (!cell->start) continue;
138 cell->link_num = link_num;
139 get_cell_width(html_context, cell->start,
140 cell->end, table->cellpadding,
141 0, 0, &cell->min_width,
142 &cell->max_width,
143 link_num, &link_num);
145 } else {
146 int col, row;
148 for (col = 0; col < table->cols; col++)
149 for (row = 0; row < table->rows; row++) {
150 struct table_cell *cell = CELL(table, col, row);
152 if (!cell->start) continue;
153 cell->link_num = link_num;
154 get_cell_width(html_context, cell->start,
155 cell->end, table->cellpadding,
156 0, 0, &cell->min_width,
157 &cell->max_width,
158 link_num, &link_num);
162 table->link_num = link_num;
165 static inline void
166 distribute_values(int *values, int count, int wanted, int *limits)
168 int i;
169 int sum = 0, d, r, t;
171 for (i = 0; i < count; i++) sum += values[i];
172 if (sum >= wanted) return;
174 again:
175 t = wanted - sum;
176 d = t / count;
177 r = t % count;
178 wanted = 0;
180 if (limits) {
181 for (i = 0; i < count; i++) {
182 int delta;
184 values[i] += d + (i < r);
186 delta = values[i] - limits[i];
187 if (delta > 0) {
188 wanted += delta;
189 values[i] = limits[i];
192 } else {
193 for (i = 0; i < count; i++) {
194 values[i] += d + (i < r);
198 if (wanted) {
199 assertm(limits != NULL, "bug in distribute_values()");
200 limits = NULL;
201 sum = 0;
202 goto again;
207 /* Returns: -1 none, 0, space, 1 line, 2 double */
208 static inline int
209 get_vline_width(struct table *table, int col)
211 int width = 0;
213 if (!col) return -1;
215 if (table->rules == TABLE_RULE_COLS || table->rules == TABLE_RULE_ALL)
216 width = table->cellspacing;
217 else if (table->rules == TABLE_RULE_GROUPS)
218 width = (col < table->columns_count && table->columns[col].group);
220 if (!width && table->cellpadding) width = -1;
222 return width;
225 static int
226 get_hline_width(struct table *table, int row)
228 if (!row) return -1;
230 if (table->rules == TABLE_RULE_ROWS || table->rules == TABLE_RULE_ALL) {
231 if (table->cellspacing || table->vcellpadding)
232 return table->cellspacing;
233 return -1;
235 } else if (table->rules == TABLE_RULE_GROUPS) {
236 int col;
238 for (col = 0; col < table->cols; col++)
239 if (CELL(table, col, row)->is_group) {
240 if (table->cellspacing || table->vcellpadding)
241 return table->cellspacing;
242 return -1;
246 return table->vcellpadding ? 0 : -1;
249 #define has_vline_width(table, col) (get_vline_width(table, col) >= 0)
250 #define has_hline_width(table, row) (get_hline_width(table, row) >= 0)
253 static int
254 get_column_widths(struct table *table)
256 int colspan;
258 if (!table->cols) return -1; /* prevents calloc(0, ...) calls */
260 if (!table->min_cols_widths) {
261 table->min_cols_widths = mem_calloc(table->cols, sizeof(*table->min_cols_widths));
262 if (!table->min_cols_widths) return -1;
265 if (!table->max_cols_widths) {
266 table->max_cols_widths = mem_calloc(table->cols, sizeof(*table->max_cols_widths));
267 if (!table->max_cols_widths) {
268 mem_free_set(&table->min_cols_widths, NULL);
269 return -1;
273 if (!table->cols_widths) {
274 table->cols_widths = mem_calloc(table->cols, sizeof(*table->cols_widths));
275 if (!table->cols_widths) {
276 mem_free_set(&table->min_cols_widths, NULL);
277 mem_free_set(&table->max_cols_widths, NULL);
278 return -1;
282 colspan = 1;
283 do {
284 int col, row;
285 int new_colspan = INT_MAX;
287 for (col = 0; col < table->cols; col++) for (row = 0; row < table->rows; row++) {
288 struct table_cell *cell = CELL(table, col, row);
290 if (cell->is_spanned || !cell->is_used) continue;
292 assertm(cell->colspan + col <= table->cols, "colspan out of table");
293 if_assert_failed return -1;
295 if (cell->colspan == colspan) {
296 int k, p = 0;
298 for (k = 1; k < colspan; k++)
299 p += has_vline_width(table, col + k);
301 distribute_values(&table->min_cols_widths[col],
302 colspan,
303 cell->min_width - p,
304 &table->max_cols_widths[col]);
306 distribute_values(&table->max_cols_widths[col],
307 colspan,
308 cell->max_width - p,
309 NULL);
311 for (k = 0; k < colspan; k++) {
312 int tmp = col + k;
314 int_lower_bound(&table->max_cols_widths[tmp],
315 table->min_cols_widths[tmp]);
318 } else if (cell->colspan > colspan
319 && cell->colspan < new_colspan) {
320 new_colspan = cell->colspan;
323 colspan = new_colspan;
324 } while (colspan != INT_MAX);
326 return 0;
329 static void
330 get_table_width(struct table *table)
332 struct table_frames table_frames;
333 int min = 0;
334 int max = 0;
335 int col;
337 for (col = 0; col < table->cols; col++) {
338 int vl = has_vline_width(table, col);
340 min += vl + table->min_cols_widths[col];
341 max += vl + table->max_cols_widths[col];
342 if (table->cols_x[col] > table->max_cols_widths[col])
343 max += table->cols_x[col];
346 get_table_frames(table, &table_frames);
348 table->min_width = min + table_frames.left + table_frames.right;
349 table->max_width = max + table_frames.left + table_frames.right;
351 assertm(min <= max, "min(%d) > max(%d)", min, max);
352 /* XXX: Recovery path? --pasky */
356 /* Initialize @width and @max_width arrays depending on the @stretch_method */
357 static inline int
358 apply_stretch_method(struct table *table, int widths[], int max_widths[],
359 int stretch_method, int max_cols_width)
361 int col, total_width = 0;
363 for (col = 0; col < table->cols; col++) {
364 switch (stretch_method) {
365 case 0:
366 if (table->cols_widths[col] >= table->cols_x[col])
367 break;
369 widths[col] = 1;
370 max_widths[col] = int_min(table->cols_x[col],
371 table->max_cols_widths[col])
372 - table->cols_widths[col];
373 if (max_widths[col] <= 0) widths[col] = 0;
374 break;
375 case 1:
376 if (table->cols_x[col] > WIDTH_RELATIVE)
377 break;
379 widths[col] = WIDTH_RELATIVE - table->cols_x[col];
380 max_widths[col] = table->max_cols_widths[col]
381 - table->cols_widths[col];
382 if (max_widths[col] <= 0) widths[col] = 0;
383 break;
384 case 2:
385 if (table->cols_x[col] != WIDTH_AUTO)
386 break;
387 /* Fall-through */
388 case 3:
389 if (table->cols_widths[col] >= table->max_cols_widths[col])
390 break;
391 max_widths[col] = table->max_cols_widths[col]
392 - table->cols_widths[col];
393 if (max_cols_width) {
394 widths[col] = 5 + table->max_cols_widths[col] * 10 / max_cols_width;
395 } else {
396 widths[col] = 1;
398 break;
399 case 4:
400 if (table->cols_x[col] < 0)
401 break;
402 widths[col] = 1;
403 max_widths[col] = table->cols_x[col]
404 - table->cols_widths[col];
405 if (max_widths[col] <= 0) widths[col] = 0;
406 break;
407 case 5:
408 if (table->cols_x[col] >= 0)
409 break;
410 if (table->cols_x[col] <= WIDTH_RELATIVE) {
411 widths[col] = WIDTH_RELATIVE - table->cols_x[col];
412 } else {
413 widths[col] = 1;
415 max_widths[col] = INT_MAX;
416 break;
417 case 6:
418 widths[col] = 1;
419 max_widths[col] = INT_MAX;
420 break;
421 default:
422 return -1;
425 total_width += widths[col];
428 return total_width;
431 /* Stretches the table columns by distributed the @spare_width among them.
432 * Returns how much of @spare_width was actually distributed. */
433 static inline int
434 stretch_columns(struct table *table, int widths[], int max_widths[],
435 int spare_width, int total_width)
437 int total_spare_width = spare_width;
439 while (spare_width) {
440 int stretch_width = 0;
441 int stretch_col = -1;
442 int col;
444 for (col = 0; col < table->cols; col++) {
445 int col_spare_width;
447 if (!widths[col])
448 continue;
450 col_spare_width = total_spare_width * widths[col] / total_width;
451 int_bounds(&col_spare_width, 1, max_widths[col]);
453 if (col_spare_width > stretch_width) {
454 stretch_width = col_spare_width;
455 stretch_col = col;
459 /* Got stretch column? */
460 if (stretch_col == -1)
461 break;
463 /* Mark the column as visited */
464 widths[stretch_col] = 0;
466 if (stretch_width > spare_width)
467 stretch_width = spare_width;
468 assertm(stretch_width >= 0, "shrinking cell");
470 table->cols_widths[stretch_col] += stretch_width;
471 spare_width -= stretch_width;
474 return total_spare_width - spare_width;
477 /* This function distributes space evenly between @table columns so that it
478 * stretches to @width. */
479 static void
480 distribute_widths(struct table *table, int width)
482 int col;
483 int spare_width = width - table->min_width;
484 int stretch_method = 0;
485 int *widths, *max_widths;
486 int max_cols_width = 0;
487 int cols_array_size;
489 if (!table->cols)
490 return;
492 assertm(spare_width >= 0, "too small width %d, required %d",
493 width, table->min_width);
495 for (col = 0; col < table->cols; col++)
496 int_lower_bound(&max_cols_width, table->max_cols_widths[col]);
498 cols_array_size = table->cols * sizeof(*table->cols_widths);
499 memcpy(table->cols_widths, table->min_cols_widths, cols_array_size);
500 table->real_width = width;
502 widths = fmem_alloc(cols_array_size);
503 if (!widths) return;
505 max_widths = fmem_alloc(cols_array_size);
506 if (!max_widths) goto free_widths;
508 while (spare_width) {
509 int stretched, total_width;
511 memset(widths, 0, cols_array_size);
512 memset(max_widths, 0, cols_array_size);
514 total_width = apply_stretch_method(table, widths, max_widths, stretch_method, max_cols_width);
515 assertm(total_width != -1, "Could not expand table");
516 if_assert_failed break;
518 if (!total_width) {
519 stretch_method++;
520 continue;
523 stretched = stretch_columns(table, widths, max_widths, spare_width, total_width);
524 if (!stretched)
525 stretch_method++;
526 else
527 spare_width -= stretched;
530 fmem_free(max_widths);
531 free_widths:
532 fmem_free(widths);
536 static int
537 get_table_cellpadding(struct html_context *html_context, struct table *table)
539 struct part *part = table->part;
540 int cpd_pass = 0, cpd_width = 0, cpd_last = table->cellpadding;
541 int margins = par_format.leftmargin + par_format.rightmargin;
543 again:
544 get_cell_widths(html_context, table);
545 if (get_column_widths(table)) return -1;
547 get_table_width(table);
549 if (!part->document && !part->box.x) {
550 if (!table->full_width)
551 int_upper_bound(&table->max_width, table->width);
552 int_lower_bound(&table->max_width, table->min_width);
554 int_lower_bound(&part->max_width, table->max_width + margins);
555 int_lower_bound(&part->box.width, table->min_width + margins);
557 return -1;
560 if (!cpd_pass && table->min_width > table->width && table->cellpadding) {
561 table->cellpadding = 0;
562 cpd_pass = 1;
563 cpd_width = table->min_width;
564 goto again;
566 if (cpd_pass == 1 && table->min_width > cpd_width) {
567 table->cellpadding = cpd_last;
568 cpd_pass = 2;
569 goto again;
572 return 0;
577 #ifdef HTML_TABLE_2ND_PASS /* This is by default ON! (<setup.h>) */
578 static void
579 check_table_widths(struct html_context *html_context, struct table *table)
581 int col, row;
582 int colspan;
583 int width, new_width;
584 int max, max_index = 0; /* go away, warning! */
585 int *widths = mem_calloc(table->cols, sizeof(*widths));
587 if (!widths) return;
589 for (row = 0; row < table->rows; row++) for (col = 0; col < table->cols; col++) {
590 struct table_cell *cell = CELL(table, col, row);
591 int k, p = 0;
593 if (!cell->start) continue;
595 for (k = 0; k < cell->colspan; k++) {
596 #ifdef __TINYC__
597 p += table->cols_widths[col + k];
598 if (k) p+= (has_vline_width(table, col + k));
599 #else
600 p += table->cols_widths[col + k] +
601 (k && has_vline_width(table, col + k));
602 #endif
605 get_cell_width(html_context, cell->start, cell->end,
606 table->cellpadding, p, 1, &cell->width,
607 NULL, cell->link_num, NULL);
609 int_upper_bound(&cell->width, p);
612 colspan = 1;
613 do {
614 int new_colspan = INT_MAX;
616 for (col = 0; col < table->cols; col++) for (row = 0; row < table->rows; row++) {
617 struct table_cell *cell = CELL(table, col, row);
619 if (!cell->start) continue;
621 assertm(cell->colspan + col <= table->cols, "colspan out of table");
622 if_assert_failed goto end;
624 if (cell->colspan == colspan) {
625 int k, p = 0;
627 for (k = 1; k < colspan; k++)
628 p += has_vline_width(table, col + k);
630 distribute_values(&widths[col],
631 colspan,
632 cell->width - p,
633 &table->max_cols_widths[col]);
635 } else if (cell->colspan > colspan
636 && cell->colspan < new_colspan) {
637 new_colspan = cell->colspan;
640 colspan = new_colspan;
641 } while (colspan != INT_MAX);
643 width = new_width = 0;
644 for (col = 0; col < table->cols; col++) {
645 width += table->cols_widths[col];
646 new_width += widths[col];
649 if (new_width > width) {
650 /* INTERNAL("new width(%d) is larger than previous(%d)", new_width, width); */
651 goto end;
654 max = -1;
655 for (col = 0; col < table->cols; col++)
656 if (table->max_cols_widths[col] > max) {
657 max = table->max_cols_widths[col];
658 max_index = col;
661 if (max != -1) {
662 widths[max_index] += width - new_width;
663 if (widths[max_index] <= table->max_cols_widths[max_index]) {
664 mem_free(table->cols_widths);
665 table->cols_widths = widths;
666 return;
670 end:
671 mem_free(widths);
673 #endif
676 static void
677 check_table_height(struct table *table, struct table_frames *frames, int y)
679 #ifndef CONFIG_FASTMEM
680 /* XXX: Cannot we simply use the @yp value we just calculated
681 * in draw_table_cells()? --pasky */
682 int old_height = table->real_height + table->part->cy;
683 int our_height = frames->top + y + frames->bottom + table->caption_height;
684 int row;
686 /* XXX: We cannot use get_table_real_height() because we are
687 * looking one row ahead - which is completely arcane to me.
688 * It makes a difference only when a table uses ruler="groups"
689 * and has non-zero cellspacing or vcellpadding. --pasky */
691 for (row = 0; row < table->rows; row++) {
692 #ifdef __TINYC__
693 our_height += table->rows_heights[row];
694 if (row < table->rows - 1 && has_hline_width(table, row + 1))
695 our_height++;
696 #else
697 our_height += table->rows_heights[row] +
698 (row < table->rows - 1 &&
699 has_hline_width(table, row + 1));
700 #endif
703 assertm(old_height == our_height, "size not matching! %d vs %d",
704 old_height, our_height);
705 #endif
708 static int
709 get_table_caption_height(struct html_context *html_context, struct table *table)
711 unsigned char *start = table->caption.start;
712 unsigned char *end = table->caption.end;
713 struct part *part;
715 if (!start || !end) return 0;
717 while (start < end && isspace(*start))
718 start++;
720 while (start < end && isspace(end[-1]))
721 end--;
723 if (start >= end) return 0;
725 part = format_html_part(html_context, start, end, table->align,
726 0, table->real_width, NULL, 0, 0,
727 NULL, table->link_num);
729 if (!part) {
730 return 0;
732 } else {
733 int height = part->box.height;
734 mem_free(part);
736 return height;
740 static int
741 get_table_real_height(struct table *table)
743 struct table_frames table_frames;
744 int height;
745 int row;
747 get_table_frames(table, &table_frames);
749 height = table_frames.top + table_frames.bottom;
750 height += table->caption_height;
751 for (row = 0; row < table->rows; row++) {
752 height += table->rows_heights[row];
753 if (row && has_hline_width(table, row))
754 height++;
757 return height;
760 static void
761 get_table_heights(struct html_context *html_context, struct table *table)
763 int rowspan;
764 int col, row;
766 table->caption_height = get_table_caption_height(html_context, table);
768 for (row = 0; row < table->rows; row++) {
769 for (col = 0; col < table->cols; col++) {
770 struct table_cell *cell = CELL(table, col, row);
771 struct part *part;
772 int width = 0, sp;
774 if (!cell->is_used || cell->is_spanned) continue;
776 for (sp = 0; sp < cell->colspan; sp++) {
777 #ifdef __TINYC__
778 width += table->cols_widths[col + sp];
779 if (sp < cell->colspan - 1)
780 width += (has_vline_width(table, col + sp + 1));
781 #else
782 width += table->cols_widths[col + sp] +
783 (sp < cell->colspan - 1 &&
784 has_vline_width(table, col + sp + 1));
785 #endif
788 part = format_cell(html_context, table, cell, NULL,
789 2, 2, width);
790 if (!part) return;
792 cell->height = part->box.height;
793 /* DBG("%d, %d.", width, cell->height); */
794 mem_free(part);
798 rowspan = 1;
799 do {
800 int new_rowspan = INT_MAX;
802 for (row = 0; row < table->rows; row++) {
803 for (col = 0; col < table->cols; col++) {
804 struct table_cell *cell = CELL(table, col, row);
806 if (!cell->is_used || cell->is_spanned) continue;
808 if (cell->rowspan == rowspan) {
809 int k, p = 0;
811 for (k = 1; k < rowspan; k++)
812 p += has_hline_width(table, row + k);
814 distribute_values(&table->rows_heights[row],
815 rowspan,
816 cell->height - p,
817 NULL);
819 } else if (cell->rowspan > rowspan &&
820 cell->rowspan < new_rowspan) {
821 new_rowspan = cell->rowspan;
826 rowspan = new_rowspan;
827 } while (rowspan != INT_MAX);
829 table->real_height = get_table_real_height(table);
832 static void
833 draw_table_cell(struct table *table, int col, int row, int x, int y,
834 struct html_context *html_context)
836 struct table_cell *cell = CELL(table, col, row);
837 struct document *document = table->part->document;
838 struct part *part;
839 int width = 0;
840 int height = 0;
841 int s, tmpy = y;
842 struct html_element *state;
844 if (!cell->start) return;
846 for (s = 0; s < cell->colspan; s++) {
847 #ifdef __TINYC__
848 width += table->cols_widths[col + s];
849 if (s < cell->colspan - 1)
850 width += (has_vline_width(table, col + s + 1));
851 #else
852 width += table->cols_widths[col + s] +
853 (s < cell->colspan - 1 &&
854 has_vline_width(table, col + s + 1));
855 #endif
858 for (s = 0; s < cell->rowspan; s++) {
859 #ifdef __TINYC__
860 height += table->rows_heights[row + s];
861 if (s < cell->rowspan - 1)
862 height += (has_hline_width(table, row + s + 1));
863 #else
864 height += table->rows_heights[row + s] +
865 (s < cell->rowspan - 1 &&
866 has_hline_width(table, row + s + 1));
867 #endif
870 state = init_html_parser_state(html_context, ELEMENT_DONT_KILL,
871 cell->align, 0, 0);
873 if (cell->is_header) format.style.attr |= AT_BOLD;
875 format.style.color.background = cell->bgcolor;
876 par_format.color.background = cell->bgcolor;
878 if (cell->valign == VALIGN_MIDDLE)
879 tmpy += (height - cell->height) / 2;
880 else if (cell->valign == VALIGN_BOTTOM)
881 tmpy += (height - cell->height);
883 part = format_cell(html_context, table, cell, document, x, tmpy, width);
884 if (part) {
885 /* The cell content doesn't necessarily fill out the whole cell
886 * height so use the calculated @height because it is an upper
887 * bound. */
888 assert(height >= cell->height);
890 /* The line expansion draws the _remaining_ background color of
891 * both untouched lines and lines that doesn't stretch the
892 * whole cell width. */
893 expand_lines(html_context, table->part, x + width - 1, y, height, cell->bgcolor);
895 if (cell->fragment_id)
896 add_fragment_identifier(html_context, part,
897 cell->fragment_id);
900 done_html_parser_state(html_context, state);
902 if (part) mem_free(part);
905 static void
906 draw_table_cells(struct table *table, int x, int y,
907 struct html_context *html_context)
909 int col, row;
910 int xp;
911 color_T bgcolor = par_format.color.background;
912 struct table_frames table_frames;
914 get_table_frames(table, &table_frames);
916 if (table->fragment_id)
917 add_fragment_identifier(html_context, table->part,
918 table->fragment_id);
920 /* Expand using the background color of the ``parent context'' all the
921 * way down the start of the left edge of the table. */
922 expand_lines(html_context, table->part, x - 1, y, table->real_height, bgcolor);
924 xp = x + table_frames.left;
925 for (col = 0; col < table->cols; col++) {
926 int yp = y + table_frames.top;
928 for (row = 0; row < table->rows; row++) {
929 int row_height = table->rows_heights[row] +
930 (row < table->rows - 1 && has_hline_width(table, row + 1));
932 draw_table_cell(table, col, row, xp, yp, html_context);
934 yp += row_height;
937 if (col < table->cols - 1) {
938 xp += table->cols_widths[col] + has_vline_width(table, col + 1);
942 /* Finish the table drawing by aligning the right and bottom edge of
943 * the table */
944 x += table->real_width - 1;
945 expand_lines(html_context, table->part, x, y, table->real_height, table->color.background);
947 /* Tables are renderer column-wise which breaks forms where the
948 * form items appears in a column before the actual form tag is
949 * parsed. Consider the folloing example:
951 * +--------+--------+ Where cell 2 has a <form>-tag
952 * | cell 1 | cell 2 | and cell 3 has an <input>-tag.
953 * +--------+--------+
954 * | cell 3 | cell 4 | The table is rendered by drawing
955 * +--------+--------+ the cells in the order: 1, 3, 2, 4.
957 * That is the <input>-tag form-item is added before the <form>-tag,
958 * which means a ``dummy'' form to hold it is added too.
959 * Calling check_html_form_hierarchy() will join the the form-item
960 * to the correct form from cell 2. */
961 check_html_form_hierarchy(table->part);
963 /* Do a sanity check whether the height is correct */
964 check_table_height(table, &table_frames, y);
968 static inline int
969 get_frame_pos(int a, int a_size, int b, int b_size)
971 assert(a >= -1 || a < a_size + 2 || b >= 0 || b <= b_size);
972 if_assert_failed return 0;
973 return a + 1 + (a_size + 2) * b;
976 #define H_FRAME_POSITION(table, col, row) frame[0][get_frame_pos(col, (table)->cols, row, (table)->rows)]
977 #define V_FRAME_POSITION(table, col, row) frame[1][get_frame_pos(row, (table)->rows, col, (table)->cols)]
979 static inline void
980 draw_frame_point(struct table *table, signed char *frame[2], int x, int y,
981 int col, int row, struct html_context *html_context)
983 static enum border_char const border_chars[81] = {
984 BORDER_NONE, BORDER_SVLINE, BORDER_DVLINE,
985 BORDER_SHLINE, BORDER_SDLCORNER, BORDER_DSDLCORNER,
986 BORDER_DHLINE, BORDER_SDDLCORNER, BORDER_DDLCORNER,
988 BORDER_SHLINE, BORDER_SDRCORNER, BORDER_DSDRCORNER,
989 BORDER_SHLINE, BORDER_SUTEE, BORDER_DSUTEE,
990 BORDER_DHLINE, BORDER_SDDLCORNER, BORDER_DDLCORNER,
992 BORDER_DHLINE, BORDER_SDDRCORNER, BORDER_DDRCORNER,
993 BORDER_DHLINE, BORDER_SDDRCORNER, BORDER_DDRCORNER,
994 BORDER_DHLINE, BORDER_SDUTEE, BORDER_DUTEE,
996 BORDER_SVLINE, BORDER_SVLINE, BORDER_DVLINE,
997 BORDER_SULCORNER, BORDER_SRTEE, BORDER_DSDLCORNER,
998 BORDER_SDULCORNER, BORDER_SDRTEE, BORDER_DDLCORNER,
1000 BORDER_SURCORNER, BORDER_SLTEE, BORDER_DSDRCORNER,
1001 BORDER_SDTEE, BORDER_SCROSS, BORDER_DSUTEE,
1002 BORDER_SDULCORNER, BORDER_SDRTEE, BORDER_DDLCORNER,
1004 BORDER_SDURCORNER, BORDER_SDLTEE, BORDER_DDRCORNER,
1005 BORDER_SDURCORNER, BORDER_SDLTEE, BORDER_DDRCORNER,
1006 BORDER_SDDTEE, BORDER_SDCROSS, BORDER_DUTEE,
1008 BORDER_DVLINE, BORDER_DVLINE, BORDER_DVLINE,
1009 BORDER_DSULCORNER, BORDER_DSULCORNER, BORDER_DSRTEE,
1010 BORDER_DULCORNER, BORDER_DULCORNER, BORDER_DRTEE,
1012 BORDER_DSURCORNER, BORDER_DSURCORNER, BORDER_DSLTEE,
1013 BORDER_DSDTEE, BORDER_DSDTEE, BORDER_DSCROSS,
1014 BORDER_DULCORNER, BORDER_DULCORNER, BORDER_DRTEE,
1016 BORDER_DURCORNER, BORDER_DURCORNER, BORDER_DLTEE,
1017 BORDER_DURCORNER, BORDER_DURCORNER, BORDER_DLTEE,
1018 BORDER_DDTEE, BORDER_DDTEE, BORDER_DCROSS,
1020 /* Note: I have no clue wether any of these names are suitable but they
1021 * should give an idea of what is going on. --jonas */
1022 signed char left = H_FRAME_POSITION(table, col - 1, row);
1023 signed char right = H_FRAME_POSITION(table, col, row);
1024 signed char top = V_FRAME_POSITION(table, col, row - 1);
1025 signed char bottom = V_FRAME_POSITION(table, col, row);
1026 int pos;
1028 if (left < 0 && right < 0 && top < 0 && bottom < 0) return;
1030 pos = int_max(top, 0)
1031 + 3 * int_max(right, 0)
1032 + 9 * int_max(left, 0)
1033 + 27 * int_max(bottom, 0);
1035 draw_frame_hchars(table->part, x, y, 1, border_chars[pos],
1036 par_format.color.background, table->color.border,
1037 html_context);
1040 static inline void
1041 draw_frame_hline(struct table *table, signed char *frame[2], int x, int y,
1042 int col, int row, struct html_context *html_context)
1044 static unsigned char const hltable[] = { ' ', BORDER_SHLINE, BORDER_DHLINE };
1045 int pos = H_FRAME_POSITION(table, col, row);
1047 assertm(pos < 3, "Horizontal table position out of bound [%d]", pos);
1048 if_assert_failed return;
1050 if (pos < 0 || table->cols_widths[col] <= 0) return;
1052 draw_frame_hchars(table->part, x, y, table->cols_widths[col], hltable[pos],
1053 par_format.color.background, table->color.border, html_context);
1056 static inline void
1057 draw_frame_vline(struct table *table, signed char *frame[2], int x, int y,
1058 int col, int row, struct html_context *html_context)
1060 static unsigned char const vltable[] = { ' ', BORDER_SVLINE, BORDER_DVLINE };
1061 int pos = V_FRAME_POSITION(table, col, row);
1063 assertm(pos < 3, "Vertical table position out of bound [%d]", pos);
1064 if_assert_failed return;
1066 if (pos < 0 || table->rows_heights[row] <= 0) return;
1068 draw_frame_vchars(table->part, x, y, table->rows_heights[row], vltable[pos],
1069 par_format.color.background, table->color.border, html_context);
1072 static inline int
1073 table_row_has_group(struct table *table, int row)
1075 int col;
1077 for (col = 0; col < table->cols; col++)
1078 if (CELL(table, col, row)->is_group)
1079 return 1;
1081 return 0;
1084 static void
1085 init_table_rules(struct table *table, signed char *frame[2])
1087 int col, row;
1089 for (row = 0; row < table->rows; row++) for (col = 0; col < table->cols; col++) {
1090 int xsp, ysp;
1091 struct table_cell *cell = CELL(table, col, row);
1093 if (!cell->is_used || cell->is_spanned) continue;
1095 xsp = cell->colspan ? cell->colspan : table->cols - col;
1096 ysp = cell->rowspan ? cell->rowspan : table->rows - row;
1098 if (table->rules != TABLE_RULE_COLS) {
1099 memset(&H_FRAME_POSITION(table, col, row), table->cellspacing, xsp);
1100 memset(&H_FRAME_POSITION(table, col, row + ysp), table->cellspacing, xsp);
1103 if (table->rules != TABLE_RULE_ROWS) {
1104 memset(&V_FRAME_POSITION(table, col, row), table->cellspacing, ysp);
1105 memset(&V_FRAME_POSITION(table, col + xsp, row), table->cellspacing, ysp);
1109 if (table->rules == TABLE_RULE_GROUPS) {
1110 for (col = 1; col < table->cols; col++) {
1111 if (table->cols_x[col])
1112 continue;
1114 memset(&V_FRAME_POSITION(table, col, 0), 0, table->rows);
1117 for (row = 1; row < table->rows; row++) {
1118 if (table_row_has_group(table, row))
1119 continue;
1121 memset(&H_FRAME_POSITION(table, 0, row), 0, table->cols);
1126 static void
1127 draw_table_frames(struct table *table, int indent, int y,
1128 struct html_context *html_context)
1130 struct table_frames table_frames;
1131 signed char *frame[2];
1132 int col, row;
1133 int cx, cy;
1134 int fh_size = (table->cols + 2) * (table->rows + 1);
1135 int fv_size = (table->cols + 1) * (table->rows + 2);
1137 frame[0] = fmem_alloc(fh_size + fv_size);
1138 if (!frame[0]) return;
1139 memset(frame[0], -1, fh_size + fv_size);
1141 frame[1] = &frame[0][fh_size];
1143 if (table->rules != TABLE_RULE_NONE)
1144 init_table_rules(table, frame);
1146 get_table_frames(table, &table_frames);
1147 memset(&H_FRAME_POSITION(table, 0, 0), table_frames.top, table->cols);
1148 memset(&H_FRAME_POSITION(table, 0, table->rows), table_frames.bottom, table->cols);
1149 memset(&V_FRAME_POSITION(table, 0, 0), table_frames.left, table->rows);
1150 memset(&V_FRAME_POSITION(table, table->cols, 0), table_frames.right, table->rows);
1152 cy = y;
1153 for (row = 0; row <= table->rows; row++) {
1154 cx = indent;
1155 if ((row > 0 && row < table->rows && has_hline_width(table, row))
1156 || (row == 0 && table_frames.top)
1157 || (row == table->rows && table_frames.bottom)) {
1158 int w = table_frames.left ? table->border : -1;
1160 for (col = 0; col < table->cols; col++) {
1161 if (col > 0)
1162 w = get_vline_width(table, col);
1164 if (w >= 0) {
1165 draw_frame_point(table, frame, cx, cy, col, row,
1166 html_context);
1167 if (row < table->rows)
1168 draw_frame_vline(table, frame, cx, cy + 1, col, row, html_context);
1169 cx++;
1172 draw_frame_hline(table, frame, cx, cy, col, row,
1173 html_context);
1174 cx += table->cols_widths[col];
1177 if (table_frames.right) {
1178 draw_frame_point(table, frame, cx, cy, col, row,
1179 html_context);
1180 if (row < table->rows)
1181 draw_frame_vline(table, frame, cx, cy + 1, col, row, html_context);
1182 cx++;
1185 cy++;
1187 } else if (row < table->rows) {
1188 for (col = 0; col <= table->cols; col++) {
1189 if ((col > 0 && col < table->cols && has_vline_width(table, col))
1190 || (col == 0 && table_frames.left)
1191 || (col == table->cols && table_frames.right)) {
1192 draw_frame_vline(table, frame, cx, cy, col, row, html_context);
1193 cx++;
1195 if (col < table->cols) cx += table->cols_widths[col];
1199 if (row < table->rows) cy += table->rows_heights[row];
1202 fmem_free(frame[0]);
1205 static void
1206 draw_table_caption(struct html_context *html_context, struct table *table,
1207 int x, int y)
1209 unsigned char *start = table->caption.start;
1210 unsigned char *end = table->caption.end;
1211 struct part *part;
1213 if (!start || !end) return;
1215 while (start < end && isspace(*start))
1216 start++;
1218 while (start < end && isspace(end[-1]))
1219 end--;
1221 if (start >= end) return;
1223 part = format_html_part(html_context, start, end, table->align,
1224 0, table->real_width, table->part->document, x, y,
1225 NULL, table->link_num);
1227 if (!part) return;
1229 table->part->cy += part->box.height;
1230 table->part->cx = -1;
1231 table->part->link_num = part->link_num;
1232 mem_free(part);
1235 /* This renders tag soup elements that the parser detected while chewing it's
1236 * way through the table HTML. */
1237 static void
1238 draw_table_bad_html(struct html_context *html_context, struct table *table)
1240 int i;
1242 for (i = 0; i < table->bad_html_size; i++) {
1243 struct html_start_end *html = &table->bad_html[i];
1244 unsigned char *start = html->start;
1245 unsigned char *end = html->end;
1247 while (start < end && isspace(*start))
1248 start++;
1250 while (start < end && isspace(end[-1]))
1251 end--;
1253 if (start >= end) continue;
1255 parse_html(start, end, table->part, NULL, html_context);
1259 static void
1260 distribute_table_widths(struct table *table)
1262 int width = table->width;
1264 if (table->min_width >= width)
1265 width = table->min_width;
1266 else if (table->max_width < width && table->full_width)
1267 width = table->max_width;
1269 distribute_widths(table, width);
1272 void
1273 format_table(unsigned char *attr, unsigned char *html, unsigned char *eof,
1274 unsigned char **end, struct html_context *html_context)
1276 struct part *part = html_context->part;
1277 struct table *table;
1278 struct node *node, *new_node;
1279 struct html_element *state;
1280 int indent, margins;
1282 html_context->table_level++;
1284 table = parse_table(html, eof, end, attr, (part->document || part->box.x),
1285 html_context);
1286 if (!table) goto ret0;
1288 table->part = part;
1290 /* XXX: This tag soup handling needs to be done outside the create
1291 * parser state. Something to do with link numbering. */
1292 /* It needs to be done _before_ processing the actual table, too.
1293 * Otherwise i.e. <form> tags between <table> and <tr> are broken. */
1294 draw_table_bad_html(html_context, table);
1296 state = init_html_parser_state(html_context, ELEMENT_DONT_KILL,
1297 ALIGN_LEFT, 0, 0);
1299 margins = par_format.leftmargin + par_format.rightmargin;
1300 if (get_table_cellpadding(html_context, table)) goto ret2;
1302 distribute_table_widths(table);
1304 if (!part->document && part->box.x == 1) {
1305 int total_width = table->real_width + margins;
1307 int_bounds(&total_width, table->real_width, par_format.width);
1308 int_lower_bound(&part->box.width, total_width);
1309 part->cy += table->real_height;
1311 goto ret2;
1314 #ifdef HTML_TABLE_2ND_PASS
1315 check_table_widths(html_context, table);
1316 #endif
1318 get_table_heights(html_context, table);
1320 if (!part->document) {
1321 int_lower_bound(&part->box.width, table->real_width + margins);
1322 part->cy += table->real_height;
1323 goto ret2;
1326 node = part->document->nodes.next;
1327 node->box.height = part->box.y - node->box.y + part->cy;
1329 indent = get_table_indent(html_context, table);
1331 /* FIXME: See bug 432. It should be possible to align the caption at
1332 * the top, bottom or the sides. */
1333 draw_table_caption(html_context, table, indent + part->box.x, part->box.y + part->cy);
1334 draw_table_cells(table, indent, part->cy, html_context);
1335 draw_table_frames(table, indent, part->cy, html_context);
1337 part->cy += table->real_height;
1338 part->cx = -1;
1340 new_node = mem_alloc(sizeof(*new_node));
1341 if (new_node) {
1342 set_box(&new_node->box, node->box.x, part->box.y + part->cy,
1343 node->box.width, 0);
1344 add_to_list(part->document->nodes, new_node);
1347 ret2:
1348 part->link_num = table->link_num;
1349 int_lower_bound(&part->box.height, part->cy);
1350 html_context->part = part; /* Might've changed in draw_table_cells(). */
1351 done_html_parser_state(html_context, state);
1353 free_table(table);
1355 ret0:
1356 html_context->table_level--;
1357 if (!html_context->table_level) free_table_cache();