1 /* HTML tables renderer */
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"
26 #include "document/html/internal.h"
31 unsigned int bottom
:1;
37 get_table_frames(struct table
*table
, struct table_frames
*result
)
39 assert(table
&& result
);
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
);
47 memset(result
, 0, sizeof(*result
));
51 /* Distance of the table from the left margin. */
53 get_table_indent(struct html_context
*html_context
, struct table
*table
)
55 int width
= par_format
.width
- table
->real_width
;
58 switch (table
->align
) {
60 indent
= (width
+ par_format
.leftmargin
- par_format
.rightmargin
) / 2;
64 indent
= width
- par_format
.rightmargin
;
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;
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
)
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
);
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
)
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
);
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
;
119 assertm(*min
<= *max
, "get_cell_width: %d > %d", *min
, *max
);
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
) {
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
,
143 link_num
, &link_num
);
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
,
158 link_num
, &link_num
);
162 table
->link_num
= link_num
;
166 distribute_values(int *values
, int count
, int wanted
, int *limits
)
169 int sum
= 0, d
, r
, t
;
171 for (i
= 0; i
< count
; i
++) sum
+= values
[i
];
172 if (sum
>= wanted
) return;
181 for (i
= 0; i
< count
; i
++) {
184 values
[i
] += d
+ (i
< r
);
186 delta
= values
[i
] - limits
[i
];
189 values
[i
] = limits
[i
];
193 for (i
= 0; i
< count
; i
++) {
194 values
[i
] += d
+ (i
< r
);
199 assertm(limits
!= NULL
, "bug in distribute_values()");
207 /* Returns: -1 none, 0, space, 1 line, 2 double */
209 get_vline_width(struct table
*table
, int col
)
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;
226 get_hline_width(struct table
*table
, int row
)
230 if (table
->rules
== TABLE_RULE_ROWS
|| table
->rules
== TABLE_RULE_ALL
) {
231 if (table
->cellspacing
|| table
->vcellpadding
)
232 return table
->cellspacing
;
235 } else if (table
->rules
== TABLE_RULE_GROUPS
) {
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
;
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)
254 get_column_widths(struct table
*table
)
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
);
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
);
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
) {
298 for (k
= 1; k
< colspan
; k
++)
299 p
+= has_vline_width(table
, col
+ k
);
301 distribute_values(&table
->min_cols_widths
[col
],
304 &table
->max_cols_widths
[col
]);
306 distribute_values(&table
->max_cols_widths
[col
],
311 for (k
= 0; k
< colspan
; 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
);
330 get_table_width(struct table
*table
)
332 struct table_frames table_frames
;
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 */
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
) {
366 if (table
->cols_widths
[col
] >= table
->cols_x
[col
])
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;
376 if (table
->cols_x
[col
] > WIDTH_RELATIVE
)
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;
385 if (table
->cols_x
[col
] != WIDTH_AUTO
)
389 if (table
->cols_widths
[col
] >= table
->max_cols_widths
[col
])
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
;
400 if (table
->cols_x
[col
] < 0)
403 max_widths
[col
] = table
->cols_x
[col
]
404 - table
->cols_widths
[col
];
405 if (max_widths
[col
] <= 0) widths
[col
] = 0;
408 if (table
->cols_x
[col
] >= 0)
410 if (table
->cols_x
[col
] <= WIDTH_RELATIVE
) {
411 widths
[col
] = WIDTH_RELATIVE
- table
->cols_x
[col
];
415 max_widths
[col
] = INT_MAX
;
419 max_widths
[col
] = INT_MAX
;
425 total_width
+= widths
[col
];
431 /* Stretches the table columns by distributed the @spare_width among them.
432 * Returns how much of @spare_width was actually distributed. */
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;
444 for (col
= 0; col
< table
->cols
; col
++) {
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
;
459 /* Got stretch column? */
460 if (stretch_col
== -1)
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. */
480 distribute_widths(struct table
*table
, int width
)
483 int spare_width
= width
- table
->min_width
;
484 int stretch_method
= 0;
485 int *widths
, *max_widths
;
486 int max_cols_width
= 0;
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
);
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;
523 stretched
= stretch_columns(table
, widths
, max_widths
, spare_width
, total_width
);
527 spare_width
-= stretched
;
530 fmem_free(max_widths
);
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
;
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
);
560 if (!cpd_pass
&& table
->min_width
> table
->width
&& table
->cellpadding
) {
561 table
->cellpadding
= 0;
563 cpd_width
= table
->min_width
;
566 if (cpd_pass
== 1 && table
->min_width
> cpd_width
) {
567 table
->cellpadding
= cpd_last
;
577 #ifdef HTML_TABLE_2ND_PASS /* This is by default ON! (<setup.h>) */
579 check_table_widths(struct html_context
*html_context
, struct table
*table
)
583 int width
, new_width
;
584 int max
, max_index
= 0; /* go away, warning! */
585 int *widths
= mem_calloc(table
->cols
, sizeof(*widths
));
589 for (row
= 0; row
< table
->rows
; row
++) for (col
= 0; col
< table
->cols
; col
++) {
590 struct table_cell
*cell
= CELL(table
, col
, row
);
593 if (!cell
->start
) continue;
595 for (k
= 0; k
< cell
->colspan
; k
++) {
597 p
+= table
->cols_widths
[col
+ k
];
598 if (k
) p
+= (has_vline_width(table
, col
+ k
));
600 p
+= table
->cols_widths
[col
+ k
] +
601 (k
&& has_vline_width(table
, col
+ k
));
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
);
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
) {
627 for (k
= 1; k
< colspan
; k
++)
628 p
+= has_vline_width(table
, col
+ k
);
630 distribute_values(&widths
[col
],
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); */
655 for (col
= 0; col
< table
->cols
; col
++)
656 if (table
->max_cols_widths
[col
] > max
) {
657 max
= table
->max_cols_widths
[col
];
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
;
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
;
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
++) {
693 our_height
+= table
->rows_heights
[row
];
694 if (row
< table
->rows
- 1 && has_hline_width(table
, row
+ 1))
697 our_height
+= table
->rows_heights
[row
] +
698 (row
< table
->rows
- 1 &&
699 has_hline_width(table
, row
+ 1));
703 assertm(old_height
== our_height
, "size not matching! %d vs %d",
704 old_height
, our_height
);
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
;
715 if (!start
|| !end
) return 0;
717 while (start
< end
&& isspace(*start
))
720 while (start
< end
&& isspace(end
[-1]))
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
);
733 int height
= part
->box
.height
;
741 get_table_real_height(struct table
*table
)
743 struct table_frames table_frames
;
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
))
761 get_table_heights(struct html_context
*html_context
, struct table
*table
)
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
);
774 if (!cell
->is_used
|| cell
->is_spanned
) continue;
776 for (sp
= 0; sp
< cell
->colspan
; sp
++) {
778 width
+= table
->cols_widths
[col
+ sp
];
779 if (sp
< cell
->colspan
- 1)
780 width
+= (has_vline_width(table
, col
+ sp
+ 1));
782 width
+= table
->cols_widths
[col
+ sp
] +
783 (sp
< cell
->colspan
- 1 &&
784 has_vline_width(table
, col
+ sp
+ 1));
788 part
= format_cell(html_context
, table
, cell
, NULL
,
792 cell
->height
= part
->box
.height
;
793 /* DBG("%d, %d.", width, cell->height); */
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
) {
811 for (k
= 1; k
< rowspan
; k
++)
812 p
+= has_hline_width(table
, row
+ k
);
814 distribute_values(&table
->rows_heights
[row
],
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
);
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
;
842 struct html_element
*state
;
844 if (!cell
->start
) return;
846 for (s
= 0; s
< cell
->colspan
; s
++) {
848 width
+= table
->cols_widths
[col
+ s
];
849 if (s
< cell
->colspan
- 1)
850 width
+= (has_vline_width(table
, col
+ s
+ 1));
852 width
+= table
->cols_widths
[col
+ s
] +
853 (s
< cell
->colspan
- 1 &&
854 has_vline_width(table
, col
+ s
+ 1));
858 for (s
= 0; s
< cell
->rowspan
; s
++) {
860 height
+= table
->rows_heights
[row
+ s
];
861 if (s
< cell
->rowspan
- 1)
862 height
+= (has_hline_width(table
, row
+ s
+ 1));
864 height
+= table
->rows_heights
[row
+ s
] +
865 (s
< cell
->rowspan
- 1 &&
866 has_hline_width(table
, row
+ s
+ 1));
870 state
= init_html_parser_state(html_context
, ELEMENT_DONT_KILL
,
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
);
885 /* The cell content doesn't necessarily fill out the whole cell
886 * height so use the calculated @height because it is an upper
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
,
900 done_html_parser_state(html_context
, state
);
902 if (part
) mem_free(part
);
906 draw_table_cells(struct table
*table
, int x
, int y
,
907 struct html_context
*html_context
)
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
,
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
);
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
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
);
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)]
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
);
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
,
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
);
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
);
1073 table_row_has_group(struct table
*table
, int row
)
1077 for (col
= 0; col
< table
->cols
; col
++)
1078 if (CELL(table
, col
, row
)->is_group
)
1085 init_table_rules(struct table
*table
, signed char *frame
[2])
1089 for (row
= 0; row
< table
->rows
; row
++) for (col
= 0; col
< table
->cols
; col
++) {
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
])
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
))
1121 memset(&H_FRAME_POSITION(table
, 0, row
), 0, table
->cols
);
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];
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
);
1153 for (row
= 0; row
<= table
->rows
; row
++) {
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
++) {
1162 w
= get_vline_width(table
, col
);
1165 draw_frame_point(table
, frame
, cx
, cy
, col
, row
,
1167 if (row
< table
->rows
)
1168 draw_frame_vline(table
, frame
, cx
, cy
+ 1, col
, row
, html_context
);
1172 draw_frame_hline(table
, frame
, cx
, cy
, col
, row
,
1174 cx
+= table
->cols_widths
[col
];
1177 if (table_frames
.right
) {
1178 draw_frame_point(table
, frame
, cx
, cy
, col
, row
,
1180 if (row
< table
->rows
)
1181 draw_frame_vline(table
, frame
, cx
, cy
+ 1, col
, row
, html_context
);
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
);
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]);
1206 draw_table_caption(struct html_context
*html_context
, struct table
*table
,
1209 unsigned char *start
= table
->caption
.start
;
1210 unsigned char *end
= table
->caption
.end
;
1213 if (!start
|| !end
) return;
1215 while (start
< end
&& isspace(*start
))
1218 while (start
< end
&& isspace(end
[-1]))
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
);
1229 table
->part
->cy
+= part
->box
.height
;
1230 table
->part
->cx
= -1;
1231 table
->part
->link_num
= part
->link_num
;
1235 /* This renders tag soup elements that the parser detected while chewing it's
1236 * way through the table HTML. */
1238 draw_table_bad_html(struct html_context
*html_context
, struct table
*table
)
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
))
1250 while (start
< end
&& isspace(end
[-1]))
1253 if (start
>= end
) continue;
1255 parse_html(start
, end
, table
->part
, NULL
, html_context
);
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
);
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
),
1286 if (!table
) goto ret0
;
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
,
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
;
1314 #ifdef HTML_TABLE_2ND_PASS
1315 check_table_widths(html_context
, table
);
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
;
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
;
1340 new_node
= mem_alloc(sizeof(*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
);
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
);
1356 html_context
->table_level
--;
1357 if (!html_context
->table_level
) free_table_cache();