2 #include "render_table.h"
7 litehtml::render_item_table::render_item_table(std::shared_ptr
<element
> _src_el
) :
8 render_item(std::move(_src_el
)),
14 int litehtml::render_item_table::_render(int x
, int y
, const containing_block_context
&containing_block_size
, formatting_context
* fmt_ctx
, bool second_pass
)
16 if (!m_grid
) return 0;
18 containing_block_context self_size
= calculate_containing_block_context(containing_block_size
);
20 // Calculate table spacing
21 int table_width_spacing
= 0;
22 if (src_el()->css().get_border_collapse() == border_collapse_separate
)
24 table_width_spacing
= m_border_spacing_x
* (m_grid
->cols_count() + 1);
28 table_width_spacing
= 0;
30 if (m_grid
->cols_count())
32 table_width_spacing
-= std::min(border_left(), m_grid
->column(0).border_left
);
33 table_width_spacing
-= std::min(border_right(), m_grid
->column(m_grid
->cols_count() - 1).border_right
);
36 for (int col
= 1; col
< m_grid
->cols_count(); col
++)
38 table_width_spacing
-= std::min(m_grid
->column(col
).border_left
, m_grid
->column(col
- 1).border_right
);
43 // Calculate the minimum content width (MCW) of each cell: the formatted content may span any number of lines but may not overflow the cell box.
44 // If the specified 'width' (W) of the cell is greater than MCW, W is the minimum cell width. A value of 'auto' means that MCW is the minimum
47 // Also, calculate the "maximum" cell width of each cell: formatting the content without breaking lines other than where explicit line breaks occur.
49 if (m_grid
->cols_count() == 1 && self_size
.width
.type
!= containing_block_context::cbc_value_type_auto
)
51 for (int row
= 0; row
< m_grid
->rows_count(); row
++)
53 table_cell
* cell
= m_grid
->cell(0, row
);
56 cell
->min_width
= cell
->max_width
= cell
->el
->render(0, 0, self_size
.new_width(self_size
.render_width
- table_width_spacing
), fmt_ctx
);
57 cell
->el
->pos().width
= cell
->min_width
- cell
->el
->content_offset_left() -
58 cell
->el
->content_offset_right();
64 for (int row
= 0; row
< m_grid
->rows_count(); row
++)
66 for (int col
= 0; col
< m_grid
->cols_count(); col
++)
68 table_cell
* cell
= m_grid
->cell(col
, row
);
71 if (!m_grid
->column(col
).css_width
.is_predefined() && m_grid
->column(col
).css_width
.units() != css_units_percentage
)
73 int css_w
= m_grid
->column(col
).css_width
.calc_percent(self_size
.width
);
74 int el_w
= cell
->el
->render(0, 0, self_size
.new_width(css_w
),fmt_ctx
);
75 cell
->min_width
= cell
->max_width
= std::max(css_w
, el_w
);
76 cell
->el
->pos().width
= cell
->min_width
- cell
->el
->content_offset_left() -
77 cell
->el
->content_offset_right();
81 // calculate minimum content width
82 cell
->min_width
= cell
->el
->render(0, 0, self_size
.new_width(cell
->el
->content_offset_width()), fmt_ctx
);
83 // calculate maximum content width
84 cell
->max_width
= cell
->el
->render(0, 0, self_size
.new_width(self_size
.render_width
- table_width_spacing
), fmt_ctx
);
91 // For each column, determine a maximum and minimum column width from the cells that span only that column.
92 // The minimum is that required by the cell with the largest minimum cell width (or the column 'width', whichever is larger).
93 // The maximum is that required by the cell with the largest maximum cell width (or the column 'width', whichever is larger).
95 for (int col
= 0; col
< m_grid
->cols_count(); col
++)
97 m_grid
->column(col
).max_width
= 0;
98 m_grid
->column(col
).min_width
= 0;
99 for (int row
= 0; row
< m_grid
->rows_count(); row
++)
101 if (m_grid
->cell(col
, row
)->colspan
<= 1)
103 m_grid
->column(col
).max_width
= std::max(m_grid
->column(col
).max_width
, m_grid
->cell(col
, row
)->max_width
);
104 m_grid
->column(col
).min_width
= std::max(m_grid
->column(col
).min_width
, m_grid
->cell(col
, row
)->min_width
);
109 // For each cell that spans more than one column, increase the minimum widths of the columns it spans so that together,
110 // they are at least as wide as the cell. Do the same for the maximum widths.
111 // If possible, widen all spanned columns by approximately the same amount.
113 for (int col
= 0; col
< m_grid
->cols_count(); col
++)
115 for (int row
= 0; row
< m_grid
->rows_count(); row
++)
117 if (m_grid
->cell(col
, row
)->colspan
> 1)
119 int max_total_width
= m_grid
->column(col
).max_width
;
120 int min_total_width
= m_grid
->column(col
).min_width
;
121 for (int col2
= col
+ 1; col2
< col
+ m_grid
->cell(col
, row
)->colspan
; col2
++)
123 max_total_width
+= m_grid
->column(col2
).max_width
;
124 min_total_width
+= m_grid
->column(col2
).min_width
;
126 if (min_total_width
< m_grid
->cell(col
, row
)->min_width
)
128 m_grid
->distribute_min_width(m_grid
->cell(col
, row
)->min_width
- min_total_width
, col
, col
+ m_grid
->cell(col
, row
)->colspan
- 1);
130 if (max_total_width
< m_grid
->cell(col
, row
)->max_width
)
132 m_grid
->distribute_max_width(m_grid
->cell(col
, row
)->max_width
- max_total_width
, col
, col
+ m_grid
->cell(col
, row
)->colspan
- 1);
138 // If the 'table' or 'inline-table' element's 'width' property has a computed value (W) other than 'auto', the used width is the
139 // greater of W, CAPMIN, and the minimum width required by all the columns plus cell spacing or borders (MIN).
140 // If the used width is greater than MIN, the extra width should be distributed over the columns.
142 // If the 'table' or 'inline-table' element has 'width: auto', the used width is the greater of the table's containing block width,
143 // CAPMIN, and MIN. However, if either CAPMIN or the maximum width required by the columns plus cell spacing or borders (MAX) is
144 // less than that of the containing block, use max(MAX, CAPMIN).
148 int min_table_width
= 0;
149 int max_table_width
= 0;
151 if (self_size
.width
.type
== containing_block_context::cbc_value_type_absolute
)
153 table_width
= m_grid
->calc_table_width(self_size
.render_width
- table_width_spacing
, false, min_table_width
, max_table_width
);
157 table_width
= m_grid
->calc_table_width(self_size
.render_width
- table_width_spacing
, self_size
.width
.type
== containing_block_context::cbc_value_type_auto
, min_table_width
, max_table_width
);
160 min_table_width
+= table_width_spacing
;
161 max_table_width
+= table_width_spacing
;
162 table_width
+= table_width_spacing
;
163 m_grid
->calc_horizontal_positions(m_borders
, src_el()->css().get_border_collapse(), m_border_spacing_x
);
165 bool row_span_found
= false;
167 // render cells with computed width
168 for (int row
= 0; row
< m_grid
->rows_count(); row
++)
170 m_grid
->row(row
).height
= 0;
171 for (int col
= 0; col
< m_grid
->cols_count(); col
++)
173 table_cell
* cell
= m_grid
->cell(col
, row
);
176 int span_col
= col
+ cell
->colspan
- 1;
177 if (span_col
>= m_grid
->cols_count())
179 span_col
= m_grid
->cols_count() - 1;
181 int cell_width
= m_grid
->column(span_col
).right
- m_grid
->column(col
).left
;
183 //if (cell->el->pos().width != cell_width - cell->el->content_offset_left() -
184 // cell->el->content_offset_right())
186 cell
->el
->render(m_grid
->column(col
).left
, 0, self_size
.new_width(cell_width
), fmt_ctx
, true);
187 cell
->el
->pos().width
= cell_width
- cell
->el
->content_offset_left() -
188 cell
->el
->content_offset_right();
192 cell->el->pos().x = m_grid->column(col).left + cell->el->content_offset_left();
195 if (cell
->rowspan
<= 1)
197 m_grid
->row(row
).height
= std::max(m_grid
->row(row
).height
, cell
->el
->height());
201 row_span_found
= true;
210 for (int col
= 0; col
< m_grid
->cols_count(); col
++)
212 for (int row
= 0; row
< m_grid
->rows_count(); row
++)
214 table_cell
* cell
= m_grid
->cell(col
, row
);
217 int span_row
= row
+ cell
->rowspan
- 1;
218 if (span_row
>= m_grid
->rows_count())
220 span_row
= m_grid
->rows_count() - 1;
225 for (int i
= row
; i
<= span_row
; i
++)
227 h
+= m_grid
->row(i
).height
;
229 if (h
< cell
->el
->height())
231 m_grid
->row(span_row
).height
+= cell
->el
->height() - h
;
239 // Calculate vertical table spacing
240 int table_height_spacing
= 0;
241 if (src_el()->css().get_border_collapse() == border_collapse_separate
)
243 table_height_spacing
= m_border_spacing_y
* (m_grid
->rows_count() + 1);
247 table_height_spacing
= 0;
249 if (m_grid
->rows_count())
251 table_height_spacing
-= std::min(border_top(), m_grid
->row(0).border_top
);
252 table_height_spacing
-= std::min(border_bottom(), m_grid
->row(m_grid
->rows_count() - 1).border_bottom
);
255 for (int row
= 1; row
< m_grid
->rows_count(); row
++)
257 table_height_spacing
-= std::min(m_grid
->row(row
).border_top
, m_grid
->row(row
- 1).border_bottom
);
262 // calculate block height
263 int block_height
= 0;
264 if(self_size
.height
.type
!= containing_block_context::cbc_value_type_auto
&& self_size
.height
> 0)
266 block_height
= self_size
.height
- (m_padding
.height() + m_borders
.height());
269 // calculate minimum height from m_css.get_min_height()
271 if (!src_el()->css().get_min_height().is_predefined() && src_el()->css().get_min_height().units() == css_units_percentage
)
273 min_height
= src_el()->css().get_min_height().calc_percent(containing_block_size
.height
);
277 min_height
= (int)src_el()->css().get_min_height().val();
280 int minimum_table_height
= std::max(block_height
, min_height
);
282 m_grid
->calc_rows_height(minimum_table_height
- table_height_spacing
, m_border_spacing_y
);
283 m_grid
->calc_vertical_positions(m_borders
, src_el()->css().get_border_collapse(), m_border_spacing_y
);
285 int table_height
= 0;
287 // place cells vertically
288 for (int col
= 0; col
< m_grid
->cols_count(); col
++)
290 for (int row
= 0; row
< m_grid
->rows_count(); row
++)
292 table_cell
* cell
= m_grid
->cell(col
, row
);
295 int span_row
= row
+ cell
->rowspan
- 1;
296 if (span_row
>= m_grid
->rows_count())
298 span_row
= m_grid
->rows_count() - 1;
300 cell
->el
->pos().y
= m_grid
->row(row
).top
+ cell
->el
->content_offset_top();
301 cell
->el
->pos().height
= m_grid
->row(span_row
).bottom
- m_grid
->row(row
).top
-
302 cell
->el
->content_offset_top() -
303 cell
->el
->content_offset_bottom();
304 table_height
= std::max(table_height
, m_grid
->row(span_row
).bottom
);
305 cell
->el
->apply_vertical_align();
310 if (src_el()->css().get_border_collapse() == border_collapse_collapse
)
312 if (m_grid
->rows_count())
314 table_height
-= std::min(border_bottom(), m_grid
->row(m_grid
->rows_count() - 1).border_bottom
);
319 table_height
+= m_border_spacing_y
;
322 // Render table captions
323 // Table border doesn't round the caption, so we have to start caption in the border position
324 int top_captions
= -border_top();
326 for (auto& caption
: m_grid
->captions())
328 if(caption
->css().get_caption_side() == caption_side_top
)
330 caption
->render(-border_left(), top_captions
, self_size
.new_width(table_width
+ border_left() + border_right()), fmt_ctx
);
331 top_captions
+= caption
->height();
337 // Add border height to get the top of cells
338 top_captions
+= border_top();
340 // Save caption height for draw_background
341 m_grid
->top_captions_height(top_captions
);
343 // Move table cells to the bottom side
344 for (int row
= 0; row
< m_grid
->rows_count(); row
++)
346 m_grid
->row(row
).el_row
->pos().y
+= top_captions
;
347 for (int col
= 0; col
< m_grid
->cols_count(); col
++)
349 table_cell
* cell
= m_grid
->cell(col
, row
);
352 cell
->el
->pos().y
+= top_captions
;
358 int bottom_captions
= 0;
360 for (auto& caption
: m_grid
->captions())
362 if(caption
->css().get_caption_side() == caption_side_bottom
)
364 caption
->render(-border_left(), table_height
+ top_captions
+ bottom_captions
, self_size
.new_width(table_width
+ border_left() + border_right()), fmt_ctx
);
365 bottom_captions
+= caption
->height();
369 m_pos
.move_to(x
+ content_offset_left(), y
+ content_offset_top());
370 m_pos
.width
= table_width
;
371 m_pos
.height
= table_height
+ top_captions
+ bottom_captions
;
373 if(self_size
.width
.type
!= containing_block_context::cbc_value_type_absolute
)
375 return std::min(table_width
, max_table_width
) + content_offset_width();
377 return table_width
+ content_offset_width();
380 std::shared_ptr
<litehtml::render_item
> litehtml::render_item_table::init()
383 m_grid
= std::unique_ptr
<table_grid
>(new table_grid());
385 go_inside_table table_selector
;
386 table_rows_selector row_selector
;
387 table_cells_selector cell_selector
;
389 elements_iterator
row_iter(false, &table_selector
, &row_selector
);
391 row_iter
.process(shared_from_this(), [&](std::shared_ptr
<render_item
>& el
, iterator_item_type item_type
)
393 m_grid
->begin_row(el
);
396 elements_iterator
cell_iter(true, &table_selector
, &cell_selector
);
397 cell_iter
.process(el
, [&](std::shared_ptr
<render_item
>& el
, iterator_item_type item_type
)
399 if(item_type
!= iterator_item_type_end_parent
)
402 m_grid
->add_cell(el
);
407 for (auto& el
: m_children
)
409 if (el
->src_el()->css().get_display() == display_table_caption
)
412 m_grid
->captions().push_back(el
);
418 if(src_el()->css().get_border_collapse() == border_collapse_separate
)
420 int font_size
= src_el()->css().get_font_size();
421 document::ptr doc
= src_el()->get_document();
422 m_border_spacing_x
= doc
->to_pixels(src_el()->css().get_border_spacing_x(), font_size
);
423 m_border_spacing_y
= doc
->to_pixels(src_el()->css().get_border_spacing_y(), font_size
);
426 m_border_spacing_x
= 0;
427 m_border_spacing_y
= 0;
430 src_el()->add_render(shared_from_this());
432 return shared_from_this();
435 void litehtml::render_item_table::draw_children(uint_ptr hdc
, int x
, int y
, const position
* clip
, draw_flag flag
, int zindex
)
439 position pos
= m_pos
;
442 for (auto& caption
: m_grid
->captions())
444 if (flag
== draw_block
)
446 caption
->src_el()->draw(hdc
, pos
.x
, pos
.y
, clip
, caption
);
448 caption
->draw_children(hdc
, pos
.x
, pos
.y
, clip
, flag
, zindex
);
450 for (int row
= 0; row
< m_grid
->rows_count(); row
++)
452 if (flag
== draw_block
)
454 m_grid
->row(row
).el_row
->src_el()->draw_background(hdc
, pos
.x
, pos
.y
, clip
, m_grid
->row(row
).el_row
);
456 for (int col
= 0; col
< m_grid
->cols_count(); col
++)
458 table_cell
* cell
= m_grid
->cell(col
, row
);
461 if (flag
== draw_block
)
463 cell
->el
->src_el()->draw(hdc
, pos
.x
, pos
.y
, clip
, cell
->el
);
465 cell
->el
->draw_children(hdc
, pos
.x
, pos
.y
, clip
, flag
, zindex
);
471 int litehtml::render_item_table::get_draw_vertical_offset()
475 return m_grid
->top_captions_height();
480 void litehtml::render_item_table_row::get_inline_boxes( position::vector
& boxes
) const
483 for(auto& el
: m_children
)
485 if(el
->src_el()->css().get_display() == display_table_cell
)
487 pos
.x
= el
->left() + el
->margin_left();
488 pos
.y
= el
->top() - m_padding
.top
- m_borders
.top
;
490 pos
.width
= el
->right() - pos
.x
- el
->margin_right() - el
->margin_left();
491 pos
.height
= el
->height() + m_padding
.top
+ m_padding
.bottom
+ m_borders
.top
+ m_borders
.bottom
;
493 boxes
.push_back(pos
);