1 /* HTML tables parser */
3 /* Note that this does *not* fit to the HTML parser infrastructure yet, it has
4 * some special custom calling conventions and is managed from
5 * document/html/tables.c. */
17 #include "document/html/parser/parse.h"
18 #include "document/html/parser/table.h"
19 #include "document/html/parser.h"
20 #include "document/options.h"
21 #include "util/color.h"
22 #include "util/conv.h"
23 #include "util/error.h"
24 #include "util/memory.h"
27 #include "document/html/internal.h"
30 #define INIT_REAL_COLS 2
31 #define INIT_REAL_ROWS 2
33 #define realloc_bad_html(bad_html, size) \
34 mem_align_alloc(bad_html, size, (size) + 1, 0xFF)
37 add_table_bad_html_start(struct table
*table
, unsigned char *start
)
39 if (table
->caption
.start
&& !table
->caption
.end
)
42 /* Either no bad html or last one not needing @end pointer */
43 if (table
->bad_html_size
44 && !table
->bad_html
[table
->bad_html_size
- 1].end
)
47 if (realloc_bad_html(&table
->bad_html
, table
->bad_html_size
))
48 table
->bad_html
[table
->bad_html_size
++].start
= start
;
52 add_table_bad_html_end(struct table
*table
, unsigned char *end
)
54 if (table
->caption
.start
&& !table
->caption
.end
) {
55 table
->caption
.end
= end
;
59 if (table
->bad_html_size
60 && !table
->bad_html
[table
->bad_html_size
- 1].end
)
61 table
->bad_html
[table
->bad_html_size
- 1].end
= end
;
66 get_bordercolor(struct html_context
*html_context
, unsigned char *a
, color_T
*rgb
)
70 if (!use_document_fg_colors(html_context
->options
))
73 at
= get_attr_val(a
, "bordercolor", html_context
->doc_cp
);
74 /* Try some other MSIE-specific attributes if any. */
76 at
= get_attr_val(a
, "bordercolorlight", html_context
->doc_cp
);
78 at
= get_attr_val(a
, "bordercolordark", html_context
->doc_cp
);
81 decode_color(at
, strlen(at
), rgb
);
86 get_align(struct html_context
*html_context
, unsigned char *attr
, int *a
)
88 unsigned char *al
= get_attr_val(attr
, "align", html_context
->doc_cp
);
92 if (!strcasecmp(al
, "left")) *a
= ALIGN_LEFT
;
93 else if (!strcasecmp(al
, "right")) *a
= ALIGN_RIGHT
;
94 else if (!strcasecmp(al
, "center")) *a
= ALIGN_CENTER
;
95 else if (!strcasecmp(al
, "justify")) *a
= ALIGN_JUSTIFY
;
96 else if (!strcasecmp(al
, "char")) *a
= ALIGN_RIGHT
; /* NOT IMPLEMENTED */
101 get_valign(struct html_context
*html_context
, unsigned char *attr
, int *a
)
103 unsigned char *al
= get_attr_val(attr
, "valign", html_context
->doc_cp
);
107 if (!strcasecmp(al
, "top")) *a
= VALIGN_TOP
;
108 else if (!strcasecmp(al
, "middle")) *a
= VALIGN_MIDDLE
;
109 else if (!strcasecmp(al
, "bottom")) *a
= VALIGN_BOTTOM
;
110 else if (!strcasecmp(al
, "baseline")) *a
= VALIGN_BASELINE
; /* NOT IMPLEMENTED */
115 get_column_width(unsigned char *attr
, int *width
, int sh
,
116 struct html_context
*html_context
)
118 unsigned char *al
= get_attr_val(attr
, "width", html_context
->doc_cp
);
124 if (len
&& al
[len
- 1] == '*') {
130 n
= strtoul(al
, (char **) &en
, 10);
131 if (!errno
&& n
>= 0 && (!*en
|| *en
== '.'))
132 *width
= WIDTH_RELATIVE
- n
;
134 int w
= get_width(attr
, "width", sh
, html_context
);
136 if (w
>= 0) *width
= w
;
142 set_table_frame(struct html_context
*html_context
, struct table
*table
,
147 if (!table
->border
) {
148 table
->frame
= TABLE_FRAME_VOID
;
152 table
->frame
= TABLE_FRAME_BOX
;
154 al
= get_attr_val(attr
, "frame", html_context
->doc_cp
);
157 if (!strcasecmp(al
, "void")) table
->frame
= TABLE_FRAME_VOID
;
158 else if (!strcasecmp(al
, "above")) table
->frame
= TABLE_FRAME_ABOVE
;
159 else if (!strcasecmp(al
, "below")) table
->frame
= TABLE_FRAME_BELOW
;
160 else if (!strcasecmp(al
, "hsides")) table
->frame
= TABLE_FRAME_HSIDES
;
161 else if (!strcasecmp(al
, "vsides")) table
->frame
= TABLE_FRAME_VSIDES
;
162 else if (!strcasecmp(al
, "lhs")) table
->frame
= TABLE_FRAME_LHS
;
163 else if (!strcasecmp(al
, "rhs")) table
->frame
= TABLE_FRAME_RHS
;
164 /* Following tests are useless since TABLE_FRAME_BOX is the default.
165 * else if (!strcasecmp(al, "box")) table->frame = TABLE_FRAME_BOX;
166 * else if (!strcasecmp(al, "border")) table->frame = TABLE_FRAME_BOX;
172 set_table_rules(struct html_context
*html_context
, struct table
*table
,
177 table
->rules
= table
->border
? TABLE_RULE_ALL
: TABLE_RULE_NONE
;
179 al
= get_attr_val(attr
, "rules", html_context
->doc_cp
);
182 if (!strcasecmp(al
, "none")) table
->rules
= TABLE_RULE_NONE
;
183 else if (!strcasecmp(al
, "groups")) table
->rules
= TABLE_RULE_GROUPS
;
184 else if (!strcasecmp(al
, "rows")) table
->rules
= TABLE_RULE_ROWS
;
185 else if (!strcasecmp(al
, "cols")) table
->rules
= TABLE_RULE_COLS
;
186 else if (!strcasecmp(al
, "all")) table
->rules
= TABLE_RULE_ALL
;
191 parse_table_attributes(struct table
*table
, unsigned char *attr
, int real
,
192 struct html_context
*html_context
)
194 table
->fragment_id
= get_attr_val(attr
, "id", html_context
->doc_cp
);
196 get_bordercolor(html_context
, attr
, &table
->bordercolor
);
198 table
->width
= get_width(attr
, "width", real
, html_context
);
199 if (table
->width
== -1) {
200 table
->width
= get_html_max_width();
201 table
->full_width
= 1;
204 /* From http://www.w3.org/TR/html4/struct/tables.html#adef-border-TABLE
205 * The following settings should be observed by user agents for
206 * backwards compatibility.
207 * Setting border="0" implies frame="void" and, unless otherwise
208 * specified, rules="none".
209 * Other values of border imply frame="border" and, unless otherwise
210 * specified, rules="all".
211 * The value "border" in the start tag of the TABLE element should be
212 * interpreted as the value of the frame attribute. It implies
213 * rules="all" and some default (non-zero) value for the border
215 table
->border
= get_num(attr
, "border", html_context
->doc_cp
);
216 if (table
->border
== -1) {
218 has_attr(attr
, "border", html_context
->doc_cp
)
219 || has_attr(attr
, "rules", html_context
->doc_cp
)
220 || has_attr(attr
, "frame", html_context
->doc_cp
);
224 int_upper_bound(&table
->border
, 2);
226 table
->cellspacing
= get_num(attr
, "cellspacing", html_context
->doc_cp
);
227 int_bounds(&table
->cellspacing
, 1, 2);
230 set_table_frame(html_context
, table
, attr
);
232 /* TODO: cellpadding may be expressed as a percentage, this is not
234 table
->cellpadding
= get_num(attr
, "cellpadding", html_context
->doc_cp
);
235 if (table
->cellpadding
== -1) {
236 table
->vcellpadding
= 0;
237 table
->cellpadding
= !!table
->border
;
239 table
->vcellpadding
= (table
->cellpadding
>= HTML_CHAR_HEIGHT
/ 2 + 1);
240 table
->cellpadding
= (table
->cellpadding
>= HTML_CHAR_WIDTH
/ 2 + 1);
243 set_table_rules(html_context
, table
, attr
);
245 table
->align
= par_format
.align
;
246 get_align(html_context
, attr
, &table
->align
);
248 table
->bgcolor
= par_format
.bgcolor
;
249 get_bgcolor(html_context
, attr
, &table
->bgcolor
);
253 static struct table
*
256 struct table
*table
= mem_calloc(1, sizeof(*table
));
258 if (!table
) return NULL
;
260 table
->cells
= mem_calloc(INIT_REAL_COLS
* INIT_REAL_ROWS
,
261 sizeof(*table
->cells
));
266 table
->real_cols
= INIT_REAL_COLS
;
267 table
->real_rows
= INIT_REAL_ROWS
;
269 table
->columns
= mem_calloc(INIT_REAL_COLS
, sizeof(*table
->columns
));
270 if (!table
->columns
) {
271 mem_free(table
->cells
);
275 table
->real_columns_count
= INIT_REAL_COLS
;
281 free_table(struct table
*table
)
285 mem_free_if(table
->min_cols_widths
);
286 mem_free_if(table
->max_cols_widths
);
287 mem_free_if(table
->cols_widths
);
288 mem_free_if(table
->rows_heights
);
289 mem_free_if(table
->fragment_id
);
290 mem_free_if(table
->cols_x
);
291 mem_free_if(table
->bad_html
);
293 for (col
= 0; col
< table
->cols
; col
++)
294 for (row
= 0; row
< table
->rows
; row
++)
295 mem_free_if(CELL(table
, col
, row
)->fragment_id
);
297 mem_free(table
->cells
);
298 mem_free(table
->columns
);
303 expand_cells(struct table
*table
, int dest_col
, int dest_row
)
305 if (dest_col
>= table
->cols
) {
307 int last_col
= table
->cols
- 1;
310 for (row
= 0; row
< table
->rows
; row
++) {
312 struct table_cell
*cellp
= CELL(table
, last_col
, row
);
314 if (cellp
->colspan
!= -1) continue;
316 for (col
= table
->cols
; col
<= dest_col
; col
++) {
317 struct table_cell
*cell
= CELL(table
, col
, row
);
320 cell
->is_spanned
= 1;
321 cell
->rowspan
= cellp
->rowspan
;
323 cell
->col
= cellp
->col
;
324 cell
->row
= cellp
->row
;
328 table
->cols
= dest_col
+ 1;
331 if (dest_row
>= table
->rows
) {
333 int last_row
= table
->rows
- 1;
336 for (col
= 0; col
< table
->cols
; col
++) {
338 struct table_cell
*cellp
= CELL(table
, col
, last_row
);
340 if (cellp
->rowspan
!= -1) continue;
342 for (row
= table
->rows
; row
<= dest_row
; row
++) {
343 struct table_cell
*cell
= CELL(table
, col
, row
);
346 cell
->is_spanned
= 1;
348 cell
->colspan
= cellp
->colspan
;
349 cell
->col
= cellp
->col
;
350 cell
->row
= cellp
->row
;
354 table
->rows
= dest_row
+ 1;
359 copy_table(struct table
*table_src
, struct table
*table_dst
)
362 int size
= sizeof(*table_dst
->cells
) * table_src
->cols
;
366 for (row
= 0; row
< table_src
->rows
; row
++) {
367 memcpy(&table_dst
->cells
[row
* table_dst
->real_cols
],
368 &table_src
->cells
[row
* table_src
->real_cols
],
374 #define SMART_RAISE_LIMIT 256*1024
376 smart_raise(int target
, int base
, int unit
, int limit
)
378 while (target
> base
) {
379 int orig_base
= base
;
381 /* Until we reach 256kb we go fast. Then we raise
382 * by 256kb amounts. */
383 if (base
< limit
/ unit
) {
386 base
+= limit
/ unit
;
389 if (base
<= orig_base
) return 0;
394 static struct table_cell
*
395 new_cell(struct table
*table
, int dest_col
, int dest_row
)
397 if (dest_col
< table
->cols
&& dest_row
< table
->rows
)
398 return CELL(table
, dest_col
, dest_row
);
404 if (dest_col
< table
->real_cols
&& dest_row
< table
->real_rows
) {
405 expand_cells(table
, dest_col
, dest_row
);
406 return CELL(table
, dest_col
, dest_row
);
409 new.real_cols
= smart_raise(dest_col
+ 1, table
->real_cols
,
411 /* assume square distribution of
413 SMART_RAISE_LIMIT
/ 2);
414 if (!new.real_cols
) return NULL
;
416 limit
= SMART_RAISE_LIMIT
417 - sizeof(*new.cells
) * new.real_cols
;
418 limit
= MAX(limit
, SMART_RAISE_LIMIT
/2);
420 new.real_rows
= smart_raise(dest_row
+ 1, table
->real_rows
,
421 sizeof(*new.cells
), limit
);
422 if (!new.real_rows
) return NULL
;
424 new.cells
= mem_calloc(new.real_cols
* new.real_rows
,
426 if (!new.cells
) return NULL
;
428 copy_table(table
, &new);
430 mem_free(table
->cells
);
431 table
->cells
= new.cells
;
432 table
->real_cols
= new.real_cols
;
433 table
->real_rows
= new.real_rows
;
438 new_columns(struct table
*table
, int span
, int width
, int align
,
439 int valign
, int group
)
441 if (table
->columns_count
+ span
> table
->real_columns_count
) {
442 int n
= table
->real_columns_count
;
443 struct table_column
*new_columns
;
445 n
= smart_raise(table
->columns_count
+ span
, n
,
446 sizeof(*new_columns
), SMART_RAISE_LIMIT
);
449 new_columns
= mem_realloc(table
->columns
, n
* sizeof(*new_columns
));
450 if (!new_columns
) return;
452 table
->real_columns_count
= n
;
453 table
->columns
= new_columns
;
457 struct table_column
*column
= &table
->columns
[table
->columns_count
++];
459 column
->align
= align
;
460 column
->valign
= valign
;
461 column
->width
= width
;
462 column
->group
= group
;
468 set_td_width(struct table
*table
, int col
, int width
, int force
)
470 if (col
>= table
->cols_x_count
) {
471 int n
= table
->cols_x_count
;
475 n
= smart_raise(col
+ 1, n
, sizeof(*new_cols_x
), SMART_RAISE_LIMIT
);
476 if (!n
&& table
->cols_x_count
) return;
479 new_cols_x
= mem_realloc(table
->cols_x
, n
* sizeof(*new_cols_x
));
480 if (!new_cols_x
) return;
482 for (i
= table
->cols_x_count
; i
< n
; i
++)
483 new_cols_x
[i
] = WIDTH_AUTO
;
484 table
->cols_x_count
= n
;
485 table
->cols_x
= new_cols_x
;
488 if (force
|| table
->cols_x
[col
] == WIDTH_AUTO
) {
489 table
->cols_x
[col
] = width
;
493 if (width
== WIDTH_AUTO
) return;
495 if (width
< 0 && table
->cols_x
[col
] >= 0) {
496 table
->cols_x
[col
] = width
;
500 if (width
>= 0 && table
->cols_x
[col
] < 0) return;
501 table
->cols_x
[col
] = (table
->cols_x
[col
] + width
) >> 1;
504 static unsigned char *
505 skip_table(unsigned char *html
, unsigned char *eof
)
511 int namelen
, closing_tag
= 0;
515 || parse_element(html
, eof
, &name
, &namelen
, NULL
,
519 if (html
>= eof
) return eof
;
521 if (!namelen
) continue;
526 if (!namelen
) continue;
530 if (!strlcasecmp(name
, namelen
, "TABLE", 5)) {
535 if (!level
) return html
;
542 parse_table(unsigned char *html
, unsigned char *eof
, unsigned char **end
,
543 unsigned char *attr
, int sh
, struct html_context
*html_context
)
546 struct table_cell
*cell
;
547 unsigned char *t_attr
, *en
, *name
;
548 unsigned char *l_fragment_id
= NULL
;
549 color_T last_bgcolor
;
552 int l_al
= ALIGN_LEFT
;
553 int l_val
= VALIGN_MIDDLE
;
554 int colspan
, rowspan
;
557 int c_al
= ALIGN_TR
, c_val
= VALIGN_TR
, c_width
= WIDTH_AUTO
, c_span
= 0;
559 int col
= 0, row
= -1;
561 int closing_tag
, is_header
;
567 if (!table
) return NULL
;
569 parse_table_attributes(table
, attr
, sh
, html_context
);
570 last_bgcolor
= table
->bgcolor
;
578 add_table_bad_html_start(table
, html
);
581 while (html
< eof
&& *html
!= '<') html
++;
584 if (in_cell
) CELL(table
, col
, row
)->end
= html
;
585 add_table_bad_html_end(table
, html
);
589 if (html
+ 2 <= eof
&& (html
[1] == '!' || html
[1] == '?')) {
590 html
= skip_comment(html
, eof
);
594 if (parse_element(html
, eof
, &name
, &namelen
, &t_attr
, &en
)) {
599 if (!namelen
) goto see
;
601 if (name
[0] == '/') {
603 if (!namelen
) goto see
;
611 if (!strlcasecmp(name
, namelen
, "TABLE", 5)) {
613 en
= skip_table(en
, eof
);
617 if (c_span
) new_columns(table
, c_span
, c_width
, c_al
, c_val
, 1);
618 if (in_cell
) CELL(table
, col
, row
)->end
= html
;
620 add_table_bad_html_end(table
, html
);
626 if (!strlcasecmp(name
, namelen
, "CAPTION", 7)) {
628 add_table_bad_html_end(table
, html
);
629 if (!table
->caption
.start
)
630 table
->caption
.start
= html
;
633 if (table
->caption
.start
&& !table
->caption
.end
)
634 table
->caption
.end
= html
;
640 if (!strlcasecmp(name
, namelen
, "COLGROUP", 8)) {
641 if (c_span
) new_columns(table
, c_span
, c_width
, c_al
, c_val
, 1);
643 add_table_bad_html_end(table
, html
);
647 c_width
= WIDTH_AUTO
;
650 get_align(html_context
, t_attr
, &c_al
);
651 get_valign(html_context
, t_attr
, &c_val
);
652 get_column_width(t_attr
, &c_width
, sh
, html_context
);
653 c_span
= get_num(t_attr
, "span", html_context
->doc_cp
);
656 else if (c_span
> HTML_MAX_COLSPAN
)
657 c_span
= HTML_MAX_COLSPAN
;
666 if (!closing_tag
&& !strlcasecmp(name
, namelen
, "COL", 3)) {
667 int sp
, width
, al
, val
;
669 add_table_bad_html_end(table
, html
);
671 sp
= get_num(t_attr
, "span", html_context
->doc_cp
);
672 if (sp
== -1) sp
= 1;
673 else if (sp
> HTML_MAX_COLSPAN
) sp
= HTML_MAX_COLSPAN
;
678 get_align(html_context
, t_attr
, &al
);
679 get_valign(html_context
, t_attr
, &val
);
680 get_column_width(t_attr
, &width
, sh
, html_context
);
681 new_columns(table
, sp
, width
, al
, val
, !!c_span
);
686 /* All following tags have T as first letter. */
687 if (toupper(name
[0]) != 'T') goto see
;
690 if (namelen
== 0) goto see
;
692 c
= toupper(name
[0]);
695 if (closing_tag
&& namelen
== 1) {
696 if (c
== 'R' || c
== 'D' || c
== 'H') {
698 new_columns(table
, c_span
, c_width
, c_al
, c_val
, 1);
701 CELL(table
, col
, row
)->end
= html
;
705 add_table_bad_html_end(table
, html
);
710 /* Beyond that point, opening tags only. */
711 if (closing_tag
) goto see
;
713 /* THEAD TBODY TFOOT */
715 && ((!strlcasecmp(name
, namelen
, "HEAD", 4)) ||
716 (!strlcasecmp(name
, namelen
, "BODY", 4)) ||
717 (!strlcasecmp(name
, namelen
, "FOOT", 4)))) {
718 if (c_span
) new_columns(table
, c_span
, c_width
, c_al
, c_val
, 1);
720 add_table_bad_html_end(table
, html
);
726 /* Beyond this point, only two letters tags. */
727 if (namelen
!= 1) goto see
;
731 if (c_span
) new_columns(table
, c_span
, c_width
, c_al
, c_val
, 1);
734 CELL(table
, col
, row
)->end
= html
;
738 add_table_bad_html_end(table
, html
);
742 l_val
= VALIGN_MIDDLE
;
743 last_bgcolor
= table
->bgcolor
;
744 get_align(html_context
, t_attr
, &l_al
);
745 get_valign(html_context
, t_attr
, &l_val
);
746 get_bgcolor(html_context
, t_attr
, &last_bgcolor
);
747 mem_free_set(&l_fragment_id
,
748 get_attr_val(t_attr
, "id", html_context
->doc_cp
));
755 is_header
= (c
== 'H');
757 if (!is_header
&& c
!= 'D')
760 if (c_span
) new_columns(table
, c_span
, c_width
, c_al
, c_val
, 1);
762 add_table_bad_html_end(table
, html
);
765 CELL(table
, col
, row
)->end
= html
;
775 cell
= new_cell(table
, col
, row
);
778 if (!cell
->is_used
) break;
779 if (cell
->colspan
== -1) goto see
;
790 cell
->valign
= l_val
;
791 cell
->fragment_id
= get_attr_val(t_attr
, "id", html_context
->doc_cp
);
792 if (!cell
->fragment_id
&& l_fragment_id
) {
793 cell
->fragment_id
= l_fragment_id
;
794 l_fragment_id
= NULL
;
797 cell
->is_header
= is_header
;
798 if (cell
->is_header
) cell
->align
= ALIGN_CENTER
;
800 if (group
== 1) cell
->is_group
= 1;
802 if (col
< table
->columns_count
) {
803 if (table
->columns
[col
].align
!= ALIGN_TR
)
804 cell
->align
= table
->columns
[col
].align
;
805 if (table
->columns
[col
].valign
!= VALIGN_TR
)
806 cell
->valign
= table
->columns
[col
].valign
;
809 cell
->bgcolor
= last_bgcolor
;
811 get_align(html_context
, t_attr
, &cell
->align
);
812 get_valign(html_context
, t_attr
, &cell
->valign
);
813 get_bgcolor(html_context
, t_attr
, &cell
->bgcolor
);
815 colspan
= get_num(t_attr
, "colspan", html_context
->doc_cp
);
816 if (colspan
== -1) colspan
= 1;
817 else if (!colspan
) colspan
= -1;
818 else if (colspan
> HTML_MAX_COLSPAN
) colspan
= HTML_MAX_COLSPAN
;
820 rowspan
= get_num(t_attr
, "rowspan", html_context
->doc_cp
);
821 if (rowspan
== -1) rowspan
= 1;
822 else if (!rowspan
) rowspan
= -1;
823 else if (rowspan
> HTML_MAX_ROWSPAN
) rowspan
= HTML_MAX_ROWSPAN
;
825 cell
->colspan
= colspan
;
826 cell
->rowspan
= rowspan
;
829 int width
= WIDTH_AUTO
;
831 get_column_width(t_attr
, &width
, sh
, html_context
);
832 if (width
!= WIDTH_AUTO
)
833 set_td_width(table
, col
, width
, 0);
837 for (i
= 1; colspan
!= -1 ? i
< colspan
: i
< cols
; i
++) {
838 struct table_cell
*span_cell
= new_cell(table
, col
+ i
, row
);
843 if (span_cell
->is_used
) {
845 for (k
= 0; k
< i
; k
++)
846 CELL(table
, col
+ k
, row
)->colspan
= colspan
;
850 span_cell
->is_used
= span_cell
->is_spanned
= 1;
851 span_cell
->rowspan
= rowspan
;
852 span_cell
->colspan
= colspan
;
853 span_cell
->col
= col
;
854 span_cell
->row
= row
;
858 maxj
= rowspan
!= -1 ? rowspan
: rows
;
859 /* Out of memory prevention, limit allocated memory to HTML_MAX_CELLS_MEMORY.
860 * Not perfect but better than nothing. */
861 if (maxj
* i
> HTML_MAX_CELLS_MEMORY
/ sizeof(*cell
))
864 for (j
= 1; j
< maxj
; j
++) {
865 for (k
= 0; k
< i
; k
++) {
866 struct table_cell
*span_cell
= new_cell(table
, col
+ k
, row
+ j
);
871 if (span_cell
->is_used
) {
874 if (span_cell
->col
== col
875 && span_cell
->row
== row
)
878 for (l
= 0; l
< k
; l
++)
879 memset(CELL(table
, col
+ l
, row
+ j
), 0,
884 for (l
= 0; l
< i
; l
++)
885 for (m
= 0; m
< j
; m
++)
886 CELL(table
, col
+ l
, row
+ m
)->rowspan
= j
;
890 span_cell
->is_used
= span_cell
->is_spanned
= 1;
891 span_cell
->rowspan
= rowspan
;
892 span_cell
->colspan
= colspan
;
893 span_cell
->col
= col
;
894 span_cell
->row
= row
;
903 mem_free_if(l_fragment_id
);
905 for (col
= 0; col
< table
->cols
; col
++) for (row
= 0; row
< table
->rows
; row
++) {
906 struct table_cell
*cell
= CELL(table
, col
, row
);
908 if (!cell
->is_spanned
) {
909 if (cell
->colspan
== -1) cell
->colspan
= table
->cols
- col
;
910 if (cell
->rowspan
== -1) cell
->rowspan
= table
->rows
- row
;
915 table
->rows_heights
= mem_calloc(table
->rows
, sizeof(*table
->rows_heights
));
916 if (!table
->rows_heights
)
919 table
->rows_heights
= NULL
;
922 for (col
= 0; col
< table
->columns_count
; col
++)
923 if (table
->columns
[col
].width
!= WIDTH_AUTO
)
924 set_td_width(table
, col
, table
->columns
[col
].width
, 1);
925 set_td_width(table
, table
->cols
, WIDTH_AUTO
, 0);