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
, "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
++) {
596 p
+= table
->cols_widths
[col
+ k
] +
597 (k
&& has_vline_width(table
, col
+ k
));
600 get_cell_width(html_context
, cell
->start
, cell
->end
,
601 table
->cellpadding
, p
, 1, &cell
->width
,
602 NULL
, cell
->link_num
, NULL
);
604 int_upper_bound(&cell
->width
, p
);
609 int new_colspan
= INT_MAX
;
611 for (col
= 0; col
< table
->cols
; col
++) for (row
= 0; row
< table
->rows
; row
++) {
612 struct table_cell
*cell
= CELL(table
, col
, row
);
614 if (!cell
->start
) continue;
616 assertm(cell
->colspan
+ col
<= table
->cols
, "colspan out of table");
617 if_assert_failed
goto end
;
619 if (cell
->colspan
== colspan
) {
622 for (k
= 1; k
< colspan
; k
++)
623 p
+= has_vline_width(table
, col
+ k
);
625 distribute_values(&widths
[col
],
628 &table
->max_cols_widths
[col
]);
630 } else if (cell
->colspan
> colspan
631 && cell
->colspan
< new_colspan
) {
632 new_colspan
= cell
->colspan
;
635 colspan
= new_colspan
;
636 } while (colspan
!= INT_MAX
);
638 width
= new_width
= 0;
639 for (col
= 0; col
< table
->cols
; col
++) {
640 width
+= table
->cols_widths
[col
];
641 new_width
+= widths
[col
];
644 if (new_width
> width
) {
645 /* INTERNAL("new width(%d) is larger than previous(%d)", new_width, width); */
650 for (col
= 0; col
< table
->cols
; col
++)
651 if (table
->max_cols_widths
[col
] > max
) {
652 max
= table
->max_cols_widths
[col
];
657 widths
[max_index
] += width
- new_width
;
658 if (widths
[max_index
] <= table
->max_cols_widths
[max_index
]) {
659 mem_free(table
->cols_widths
);
660 table
->cols_widths
= widths
;
672 check_table_height(struct table
*table
, struct table_frames
*frames
, int y
)
674 #ifndef CONFIG_FASTMEM
675 /* XXX: Cannot we simply use the @yp value we just calculated
676 * in draw_table_cells()? --pasky */
677 int old_height
= table
->real_height
+ table
->part
->cy
;
678 int our_height
= frames
->top
+ y
+ frames
->bottom
+ table
->caption_height
;
681 /* XXX: We cannot use get_table_real_height() because we are
682 * looking one row ahead - which is completely arcane to me.
683 * It makes a difference only when a table uses ruler="groups"
684 * and has non-zero cellspacing or vcellpadding. --pasky */
686 for (row
= 0; row
< table
->rows
; row
++) {
687 our_height
+= table
->rows_heights
[row
] +
688 (row
< table
->rows
- 1 &&
689 has_hline_width(table
, row
+ 1));
692 assertm(old_height
== our_height
, "size not matching! %d vs %d",
693 old_height
, our_height
);
698 get_table_caption_height(struct html_context
*html_context
, struct table
*table
)
700 unsigned char *start
= table
->caption
.start
;
701 unsigned char *end
= table
->caption
.end
;
704 if (!start
|| !end
) return 0;
706 while (start
< end
&& isspace(*start
))
709 while (start
< end
&& isspace(end
[-1]))
712 if (start
>= end
) return 0;
714 part
= format_html_part(html_context
, start
, end
, table
->align
,
715 0, table
->real_width
, NULL
, 0, 0,
716 NULL
, table
->link_num
);
722 int height
= part
->box
.height
;
730 get_table_real_height(struct table
*table
)
732 struct table_frames table_frames
;
736 get_table_frames(table
, &table_frames
);
738 height
= table_frames
.top
+ table_frames
.bottom
;
739 height
+= table
->caption_height
;
740 for (row
= 0; row
< table
->rows
; row
++) {
741 height
+= table
->rows_heights
[row
];
742 if (row
&& has_hline_width(table
, row
))
750 get_table_heights(struct html_context
*html_context
, struct table
*table
)
755 table
->caption_height
= get_table_caption_height(html_context
, table
);
757 for (row
= 0; row
< table
->rows
; row
++) {
758 for (col
= 0; col
< table
->cols
; col
++) {
759 struct table_cell
*cell
= CELL(table
, col
, row
);
763 if (!cell
->is_used
|| cell
->is_spanned
) continue;
765 for (sp
= 0; sp
< cell
->colspan
; sp
++) {
766 width
+= table
->cols_widths
[col
+ sp
] +
767 (sp
< cell
->colspan
- 1 &&
768 has_vline_width(table
, col
+ sp
+ 1));
771 part
= format_cell(html_context
, table
, cell
, NULL
,
775 cell
->height
= part
->box
.height
;
776 /* DBG("%d, %d.", width, cell->height); */
783 int new_rowspan
= INT_MAX
;
785 for (row
= 0; row
< table
->rows
; row
++) {
786 for (col
= 0; col
< table
->cols
; col
++) {
787 struct table_cell
*cell
= CELL(table
, col
, row
);
789 if (!cell
->is_used
|| cell
->is_spanned
) continue;
791 if (cell
->rowspan
== rowspan
) {
794 for (k
= 1; k
< rowspan
; k
++)
795 p
+= has_hline_width(table
, row
+ k
);
797 distribute_values(&table
->rows_heights
[row
],
802 } else if (cell
->rowspan
> rowspan
&&
803 cell
->rowspan
< new_rowspan
) {
804 new_rowspan
= cell
->rowspan
;
809 rowspan
= new_rowspan
;
810 } while (rowspan
!= INT_MAX
);
812 table
->real_height
= get_table_real_height(table
);
816 draw_table_cell(struct table
*table
, int col
, int row
, int x
, int y
,
817 struct html_context
*html_context
)
819 struct table_cell
*cell
= CELL(table
, col
, row
);
820 struct document
*document
= table
->part
->document
;
825 struct html_element
*state
;
827 if (!cell
->start
) return;
829 for (s
= 0; s
< cell
->colspan
; s
++) {
830 width
+= table
->cols_widths
[col
+ s
] +
831 (s
< cell
->colspan
- 1 &&
832 has_vline_width(table
, col
+ s
+ 1));
835 for (s
= 0; s
< cell
->rowspan
; s
++) {
836 height
+= table
->rows_heights
[row
+ s
] +
837 (s
< cell
->rowspan
- 1 &&
838 has_hline_width(table
, row
+ s
+ 1));
841 state
= init_html_parser_state(html_context
, ELEMENT_DONT_KILL
,
844 if (cell
->is_header
) format
.style
.attr
|= AT_BOLD
;
846 format
.style
.bg
= cell
->bgcolor
;
847 par_format
.bgcolor
= cell
->bgcolor
;
849 if (cell
->valign
== VALIGN_MIDDLE
)
850 tmpy
+= (height
- cell
->height
) / 2;
851 else if (cell
->valign
== VALIGN_BOTTOM
)
852 tmpy
+= (height
- cell
->height
);
854 part
= format_cell(html_context
, table
, cell
, document
, x
, tmpy
, width
);
856 /* The cell content doesn't necessarily fill out the whole cell
857 * height so use the calculated @height because it is an upper
859 assert(height
>= cell
->height
);
861 /* The line expansion draws the _remaining_ background color of
862 * both untouched lines and lines that doesn't stretch the
863 * whole cell width. */
864 expand_lines(html_context
, table
->part
, x
+ width
- 1, y
, height
, cell
->bgcolor
);
866 if (cell
->fragment_id
)
867 add_fragment_identifier(html_context
, part
,
871 done_html_parser_state(html_context
, state
);
873 if (part
) mem_free(part
);
877 draw_table_cells(struct table
*table
, int x
, int y
,
878 struct html_context
*html_context
)
882 color_T bgcolor
= par_format
.bgcolor
;
883 struct table_frames table_frames
;
885 get_table_frames(table
, &table_frames
);
887 if (table
->fragment_id
)
888 add_fragment_identifier(html_context
, table
->part
,
891 /* Expand using the background color of the ``parent context'' all the
892 * way down the start of the left edge of the table. */
893 expand_lines(html_context
, table
->part
, x
- 1, y
, table
->real_height
, bgcolor
);
895 xp
= x
+ table_frames
.left
;
896 for (col
= 0; col
< table
->cols
; col
++) {
897 int yp
= y
+ table_frames
.top
;
899 for (row
= 0; row
< table
->rows
; row
++) {
900 int row_height
= table
->rows_heights
[row
] +
901 (row
< table
->rows
- 1 && has_hline_width(table
, row
+ 1));
903 draw_table_cell(table
, col
, row
, xp
, yp
, html_context
);
908 if (col
< table
->cols
- 1) {
909 xp
+= table
->cols_widths
[col
] + has_vline_width(table
, col
+ 1);
913 /* Finish the table drawing by aligning the right and bottom edge of
915 x
+= table
->real_width
- 1;
916 expand_lines(html_context
, table
->part
, x
, y
, table
->real_height
, table
->bgcolor
);
918 /* Tables are renderer column-wise which breaks forms where the
919 * form items appears in a column before the actual form tag is
920 * parsed. Consider the folloing example:
922 * +--------+--------+ Where cell 2 has a <form>-tag
923 * | cell 1 | cell 2 | and cell 3 has an <input>-tag.
924 * +--------+--------+
925 * | cell 3 | cell 4 | The table is rendered by drawing
926 * +--------+--------+ the cells in the order: 1, 3, 2, 4.
928 * That is the <input>-tag form-item is added before the <form>-tag,
929 * which means a ``dummy'' form to hold it is added too.
930 * Calling check_html_form_hierarchy() will join the the form-item
931 * to the correct form from cell 2. */
932 check_html_form_hierarchy(table
->part
);
934 /* Do a sanity check whether the height is correct */
935 check_table_height(table
, &table_frames
, y
);
940 get_frame_pos(int a
, int a_size
, int b
, int b_size
)
942 assert(a
>= -1 || a
< a_size
+ 2 || b
>= 0 || b
<= b_size
);
943 if_assert_failed
return 0;
944 return a
+ 1 + (a_size
+ 2) * b
;
947 #define H_FRAME_POSITION(table, col, row) frame[0][get_frame_pos(col, (table)->cols, row, (table)->rows)]
948 #define V_FRAME_POSITION(table, col, row) frame[1][get_frame_pos(row, (table)->rows, col, (table)->cols)]
951 draw_frame_point(struct table
*table
, signed char *frame
[2], int x
, int y
,
952 int col
, int row
, struct html_context
*html_context
)
954 static enum border_char
const border_chars
[81] = {
955 BORDER_NONE
, BORDER_SVLINE
, BORDER_DVLINE
,
956 BORDER_SHLINE
, BORDER_SDLCORNER
, BORDER_DSDLCORNER
,
957 BORDER_DHLINE
, BORDER_SDDLCORNER
, BORDER_DDLCORNER
,
959 BORDER_SHLINE
, BORDER_SDRCORNER
, BORDER_DSDRCORNER
,
960 BORDER_SHLINE
, BORDER_SUTEE
, BORDER_DSUTEE
,
961 BORDER_DHLINE
, BORDER_SDDLCORNER
, BORDER_DDLCORNER
,
963 BORDER_DHLINE
, BORDER_SDDRCORNER
, BORDER_DDRCORNER
,
964 BORDER_DHLINE
, BORDER_SDDRCORNER
, BORDER_DDRCORNER
,
965 BORDER_DHLINE
, BORDER_SDUTEE
, BORDER_DUTEE
,
967 BORDER_SVLINE
, BORDER_SVLINE
, BORDER_DVLINE
,
968 BORDER_SULCORNER
, BORDER_SRTEE
, BORDER_DSDLCORNER
,
969 BORDER_SDULCORNER
, BORDER_SDRTEE
, BORDER_DDLCORNER
,
971 BORDER_SURCORNER
, BORDER_SLTEE
, BORDER_DSDRCORNER
,
972 BORDER_SDTEE
, BORDER_SCROSS
, BORDER_DSUTEE
,
973 BORDER_SDULCORNER
, BORDER_SDRTEE
, BORDER_DDLCORNER
,
975 BORDER_SDURCORNER
, BORDER_SDLTEE
, BORDER_DDRCORNER
,
976 BORDER_SDURCORNER
, BORDER_SDLTEE
, BORDER_DDRCORNER
,
977 BORDER_SDDTEE
, BORDER_SDCROSS
, BORDER_DUTEE
,
979 BORDER_DVLINE
, BORDER_DVLINE
, BORDER_DVLINE
,
980 BORDER_DSULCORNER
, BORDER_DSULCORNER
, BORDER_DSRTEE
,
981 BORDER_DULCORNER
, BORDER_DULCORNER
, BORDER_DRTEE
,
983 BORDER_DSURCORNER
, BORDER_DSURCORNER
, BORDER_DSLTEE
,
984 BORDER_DSDTEE
, BORDER_DSDTEE
, BORDER_DSCROSS
,
985 BORDER_DULCORNER
, BORDER_DULCORNER
, BORDER_DRTEE
,
987 BORDER_DURCORNER
, BORDER_DURCORNER
, BORDER_DLTEE
,
988 BORDER_DURCORNER
, BORDER_DURCORNER
, BORDER_DLTEE
,
989 BORDER_DDTEE
, BORDER_DDTEE
, BORDER_DCROSS
,
991 /* Note: I have no clue wether any of these names are suitable but they
992 * should give an idea of what is going on. --jonas */
993 signed char left
= H_FRAME_POSITION(table
, col
- 1, row
);
994 signed char right
= H_FRAME_POSITION(table
, col
, row
);
995 signed char top
= V_FRAME_POSITION(table
, col
, row
- 1);
996 signed char bottom
= V_FRAME_POSITION(table
, col
, row
);
999 if (left
< 0 && right
< 0 && top
< 0 && bottom
< 0) return;
1001 pos
= int_max(top
, 0)
1002 + 3 * int_max(right
, 0)
1003 + 9 * int_max(left
, 0)
1004 + 27 * int_max(bottom
, 0);
1006 draw_frame_hchars(table
->part
, x
, y
, 1, border_chars
[pos
],
1007 par_format
.bgcolor
, table
->bordercolor
, html_context
);
1011 draw_frame_hline(struct table
*table
, signed char *frame
[2], int x
, int y
,
1012 int col
, int row
, struct html_context
*html_context
)
1014 static unsigned char const hltable
[] = { ' ', BORDER_SHLINE
, BORDER_DHLINE
};
1015 int pos
= H_FRAME_POSITION(table
, col
, row
);
1017 assertm(pos
< 3, "Horizontal table position out of bound [%d]", pos
);
1018 if_assert_failed
return;
1020 if (pos
< 0 || table
->cols_widths
[col
] <= 0) return;
1022 draw_frame_hchars(table
->part
, x
, y
, table
->cols_widths
[col
], hltable
[pos
],
1023 par_format
.bgcolor
, table
->bordercolor
, html_context
);
1027 draw_frame_vline(struct table
*table
, signed char *frame
[2], int x
, int y
,
1028 int col
, int row
, struct html_context
*html_context
)
1030 static unsigned char const vltable
[] = { ' ', BORDER_SVLINE
, BORDER_DVLINE
};
1031 int pos
= V_FRAME_POSITION(table
, col
, row
);
1033 assertm(pos
< 3, "Vertical table position out of bound [%d]", pos
);
1034 if_assert_failed
return;
1036 if (pos
< 0 || table
->rows_heights
[row
] <= 0) return;
1038 draw_frame_vchars(table
->part
, x
, y
, table
->rows_heights
[row
], vltable
[pos
],
1039 par_format
.bgcolor
, table
->bordercolor
, html_context
);
1043 table_row_has_group(struct table
*table
, int row
)
1047 for (col
= 0; col
< table
->cols
; col
++)
1048 if (CELL(table
, col
, row
)->is_group
)
1055 init_table_rules(struct table
*table
, signed char *frame
[2])
1059 for (row
= 0; row
< table
->rows
; row
++) for (col
= 0; col
< table
->cols
; col
++) {
1061 struct table_cell
*cell
= CELL(table
, col
, row
);
1063 if (!cell
->is_used
|| cell
->is_spanned
) continue;
1065 xsp
= cell
->colspan
? cell
->colspan
: table
->cols
- col
;
1066 ysp
= cell
->rowspan
? cell
->rowspan
: table
->rows
- row
;
1068 if (table
->rules
!= TABLE_RULE_COLS
) {
1069 memset(&H_FRAME_POSITION(table
, col
, row
), table
->cellspacing
, xsp
);
1070 memset(&H_FRAME_POSITION(table
, col
, row
+ ysp
), table
->cellspacing
, xsp
);
1073 if (table
->rules
!= TABLE_RULE_ROWS
) {
1074 memset(&V_FRAME_POSITION(table
, col
, row
), table
->cellspacing
, ysp
);
1075 memset(&V_FRAME_POSITION(table
, col
+ xsp
, row
), table
->cellspacing
, ysp
);
1079 if (table
->rules
== TABLE_RULE_GROUPS
) {
1080 for (col
= 1; col
< table
->cols
; col
++) {
1081 if (table
->cols_x
[col
])
1084 memset(&V_FRAME_POSITION(table
, col
, 0), 0, table
->rows
);
1087 for (row
= 1; row
< table
->rows
; row
++) {
1088 if (table_row_has_group(table
, row
))
1091 memset(&H_FRAME_POSITION(table
, 0, row
), 0, table
->cols
);
1097 draw_table_frames(struct table
*table
, int indent
, int y
,
1098 struct html_context
*html_context
)
1100 struct table_frames table_frames
;
1101 signed char *frame
[2];
1104 int fh_size
= (table
->cols
+ 2) * (table
->rows
+ 1);
1105 int fv_size
= (table
->cols
+ 1) * (table
->rows
+ 2);
1107 frame
[0] = fmem_alloc(fh_size
+ fv_size
);
1108 if (!frame
[0]) return;
1109 memset(frame
[0], -1, fh_size
+ fv_size
);
1111 frame
[1] = &frame
[0][fh_size
];
1113 if (table
->rules
!= TABLE_RULE_NONE
)
1114 init_table_rules(table
, frame
);
1116 get_table_frames(table
, &table_frames
);
1117 memset(&H_FRAME_POSITION(table
, 0, 0), table_frames
.top
, table
->cols
);
1118 memset(&H_FRAME_POSITION(table
, 0, table
->rows
), table_frames
.bottom
, table
->cols
);
1119 memset(&V_FRAME_POSITION(table
, 0, 0), table_frames
.left
, table
->rows
);
1120 memset(&V_FRAME_POSITION(table
, table
->cols
, 0), table_frames
.right
, table
->rows
);
1123 for (row
= 0; row
<= table
->rows
; row
++) {
1125 if ((row
> 0 && row
< table
->rows
&& has_hline_width(table
, row
))
1126 || (row
== 0 && table_frames
.top
)
1127 || (row
== table
->rows
&& table_frames
.bottom
)) {
1128 int w
= table_frames
.left
? table
->border
: -1;
1130 for (col
= 0; col
< table
->cols
; col
++) {
1132 w
= get_vline_width(table
, col
);
1135 draw_frame_point(table
, frame
, cx
, cy
, col
, row
,
1137 if (row
< table
->rows
)
1138 draw_frame_vline(table
, frame
, cx
, cy
+ 1, col
, row
, html_context
);
1142 draw_frame_hline(table
, frame
, cx
, cy
, col
, row
,
1144 cx
+= table
->cols_widths
[col
];
1147 if (table_frames
.right
) {
1148 draw_frame_point(table
, frame
, cx
, cy
, col
, row
,
1150 if (row
< table
->rows
)
1151 draw_frame_vline(table
, frame
, cx
, cy
+ 1, col
, row
, html_context
);
1157 } else if (row
< table
->rows
) {
1158 for (col
= 0; col
<= table
->cols
; col
++) {
1159 if ((col
> 0 && col
< table
->cols
&& has_vline_width(table
, col
))
1160 || (col
== 0 && table_frames
.left
)
1161 || (col
== table
->cols
&& table_frames
.right
)) {
1162 draw_frame_vline(table
, frame
, cx
, cy
, col
, row
, html_context
);
1165 if (col
< table
->cols
) cx
+= table
->cols_widths
[col
];
1169 if (row
< table
->rows
) cy
+= table
->rows_heights
[row
];
1172 fmem_free(frame
[0]);
1176 draw_table_caption(struct html_context
*html_context
, struct table
*table
,
1179 unsigned char *start
= table
->caption
.start
;
1180 unsigned char *end
= table
->caption
.end
;
1183 if (!start
|| !end
) return;
1185 while (start
< end
&& isspace(*start
))
1188 while (start
< end
&& isspace(end
[-1]))
1191 if (start
>= end
) return;
1193 part
= format_html_part(html_context
, start
, end
, table
->align
,
1194 0, table
->real_width
, table
->part
->document
, x
, y
,
1195 NULL
, table
->link_num
);
1199 table
->part
->cy
+= part
->box
.height
;
1200 table
->part
->cx
= -1;
1201 table
->part
->link_num
= part
->link_num
;
1205 /* This renders tag soup elements that the parser detected while chewing it's
1206 * way through the table HTML. */
1208 draw_table_bad_html(struct html_context
*html_context
, struct table
*table
)
1212 for (i
= 0; i
< table
->bad_html_size
; i
++) {
1213 struct html_start_end
*html
= &table
->bad_html
[i
];
1214 unsigned char *start
= html
->start
;
1215 unsigned char *end
= html
->end
;
1217 while (start
< end
&& isspace(*start
))
1220 while (start
< end
&& isspace(end
[-1]))
1223 if (start
>= end
) continue;
1225 parse_html(start
, end
, table
->part
, NULL
, html_context
);
1230 distribute_table_widths(struct table
*table
)
1232 int width
= table
->width
;
1234 if (table
->min_width
>= width
)
1235 width
= table
->min_width
;
1236 else if (table
->max_width
< width
&& table
->full_width
)
1237 width
= table
->max_width
;
1239 distribute_widths(table
, width
);
1243 format_table(unsigned char *attr
, unsigned char *html
, unsigned char *eof
,
1244 unsigned char **end
, struct html_context
*html_context
)
1246 struct part
*part
= html_context
->part
;
1247 struct table
*table
;
1248 struct node
*node
, *new_node
;
1249 struct html_element
*state
;
1250 int indent
, margins
;
1252 html_context
->table_level
++;
1254 table
= parse_table(html
, eof
, end
, attr
, (part
->document
|| part
->box
.x
),
1256 if (!table
) goto ret0
;
1260 /* XXX: This tag soup handling needs to be done outside the create
1261 * parser state. Something to do with link numbering. */
1262 /* It needs to be done _before_ processing the actual table, too.
1263 * Otherwise i.e. <form> tags between <table> and <tr> are broken. */
1264 draw_table_bad_html(html_context
, table
);
1266 state
= init_html_parser_state(html_context
, ELEMENT_DONT_KILL
,
1269 margins
= par_format
.leftmargin
+ par_format
.rightmargin
;
1270 if (get_table_cellpadding(html_context
, table
)) goto ret2
;
1272 distribute_table_widths(table
);
1274 if (!part
->document
&& part
->box
.x
== 1) {
1275 int total_width
= table
->real_width
+ margins
;
1277 int_bounds(&total_width
, table
->real_width
, par_format
.width
);
1278 int_lower_bound(&part
->box
.width
, total_width
);
1279 part
->cy
+= table
->real_height
;
1284 #ifdef HTML_TABLE_2ND_PASS
1285 check_table_widths(html_context
, table
);
1288 get_table_heights(html_context
, table
);
1290 if (!part
->document
) {
1291 int_lower_bound(&part
->box
.width
, table
->real_width
+ margins
);
1292 part
->cy
+= table
->real_height
;
1296 node
= part
->document
->nodes
.next
;
1297 node
->box
.height
= part
->box
.y
- node
->box
.y
+ part
->cy
;
1299 indent
= get_table_indent(html_context
, table
);
1301 /* FIXME: See bug 432. It should be possible to align the caption at
1302 * the top, bottom or the sides. */
1303 draw_table_caption(html_context
, table
, indent
+ part
->box
.x
, part
->box
.y
+ part
->cy
);
1304 draw_table_cells(table
, indent
, part
->cy
, html_context
);
1305 draw_table_frames(table
, indent
, part
->cy
, html_context
);
1307 part
->cy
+= table
->real_height
;
1310 new_node
= mem_alloc(sizeof(*new_node
));
1312 set_box(&new_node
->box
, node
->box
.x
, part
->box
.y
+ part
->cy
,
1313 node
->box
.width
, 0);
1314 add_to_list(part
->document
->nodes
, new_node
);
1318 part
->link_num
= table
->link_num
;
1319 int_lower_bound(&part
->box
.height
, part
->cy
);
1320 html_context
->part
= part
; /* Might've changed in draw_table_cells(). */
1321 done_html_parser_state(html_context
, state
);
1326 html_context
->table_level
--;
1327 if (!html_context
->table_level
) free_table_cache();