summaryrefslogtreecommitdiff
path: root/libs/litehtml/src/render_table.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/litehtml/src/render_table.cpp')
-rw-r--r--libs/litehtml/src/render_table.cpp496
1 files changed, 496 insertions, 0 deletions
diff --git a/libs/litehtml/src/render_table.cpp b/libs/litehtml/src/render_table.cpp
new file mode 100644
index 0000000000..543749ed12
--- /dev/null
+++ b/libs/litehtml/src/render_table.cpp
@@ -0,0 +1,496 @@
+#include "html.h"
+#include "render_table.h"
+#include "document.h"
+#include "iterators.h"
+
+
+litehtml::render_item_table::render_item_table(std::shared_ptr<element> _src_el) :
+ render_item(std::move(_src_el)),
+ m_border_spacing_x(0),
+ m_border_spacing_y(0)
+{
+}
+
+int litehtml::render_item_table::_render(int x, int y, const containing_block_context &containing_block_size, formatting_context* fmt_ctx, bool /*second_pass*/)
+{
+ if (!m_grid) return 0;
+
+ containing_block_context self_size = calculate_containing_block_context(containing_block_size);
+
+ // Calculate table spacing
+ int table_width_spacing = 0;
+ if (src_el()->css().get_border_collapse() == border_collapse_separate)
+ {
+ table_width_spacing = m_border_spacing_x * (m_grid->cols_count() + 1);
+ }
+ else
+ {
+ table_width_spacing = 0;
+
+ if (m_grid->cols_count())
+ {
+ table_width_spacing -= std::min(border_left(), m_grid->column(0).border_left);
+ table_width_spacing -= std::min(border_right(), m_grid->column(m_grid->cols_count() - 1).border_right);
+ }
+
+ for (int col = 1; col < m_grid->cols_count(); col++)
+ {
+ table_width_spacing -= std::min(m_grid->column(col).border_left, m_grid->column(col - 1).border_right);
+ }
+ }
+
+
+ // 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.
+ // 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
+ // cell width.
+ //
+ // Also, calculate the "maximum" cell width of each cell: formatting the content without breaking lines other than where explicit line breaks occur.
+
+ if (m_grid->cols_count() == 1 && self_size.width.type != containing_block_context::cbc_value_type_auto)
+ {
+ for (int row = 0; row < m_grid->rows_count(); row++)
+ {
+ table_cell* cell = m_grid->cell(0, row);
+ if (cell && cell->el)
+ {
+ cell->min_width = cell->max_width = cell->el->render(0, 0, self_size.new_width(self_size.render_width - table_width_spacing), fmt_ctx);
+ cell->el->pos().width = cell->min_width - cell->el->content_offset_left() -
+ cell->el->content_offset_right();
+ }
+ }
+ }
+ else
+ {
+ for (int row = 0; row < m_grid->rows_count(); row++)
+ {
+ for (int col = 0; col < m_grid->cols_count(); col++)
+ {
+ table_cell* cell = m_grid->cell(col, row);
+ if (cell && cell->el)
+ {
+ if (!m_grid->column(col).css_width.is_predefined() && m_grid->column(col).css_width.units() != css_units_percentage)
+ {
+ int css_w = m_grid->column(col).css_width.calc_percent(self_size.width);
+ int el_w = cell->el->render(0, 0, self_size.new_width(css_w),fmt_ctx);
+ cell->min_width = cell->max_width = std::max(css_w, el_w);
+ cell->el->pos().width = cell->min_width - cell->el->content_offset_left() -
+ cell->el->content_offset_right();
+ }
+ else
+ {
+ // calculate minimum content width
+ cell->min_width = cell->el->render(0, 0, self_size.new_width(cell->el->content_offset_width()), fmt_ctx);
+ // calculate maximum content width
+ cell->max_width = cell->el->render(0, 0, self_size.new_width(self_size.render_width - table_width_spacing), fmt_ctx);
+ }
+ }
+ }
+ }
+ }
+
+ // For each column, determine a maximum and minimum column width from the cells that span only that column.
+ // The minimum is that required by the cell with the largest minimum cell width (or the column 'width', whichever is larger).
+ // The maximum is that required by the cell with the largest maximum cell width (or the column 'width', whichever is larger).
+
+ for (int col = 0; col < m_grid->cols_count(); col++)
+ {
+ m_grid->column(col).max_width = 0;
+ m_grid->column(col).min_width = 0;
+ for (int row = 0; row < m_grid->rows_count(); row++)
+ {
+ if (m_grid->cell(col, row)->colspan <= 1)
+ {
+ m_grid->column(col).max_width = std::max(m_grid->column(col).max_width, m_grid->cell(col, row)->max_width);
+ m_grid->column(col).min_width = std::max(m_grid->column(col).min_width, m_grid->cell(col, row)->min_width);
+ }
+ }
+ }
+
+ // For each cell that spans more than one column, increase the minimum widths of the columns it spans so that together,
+ // they are at least as wide as the cell. Do the same for the maximum widths.
+ // If possible, widen all spanned columns by approximately the same amount.
+
+ for (int col = 0; col < m_grid->cols_count(); col++)
+ {
+ for (int row = 0; row < m_grid->rows_count(); row++)
+ {
+ if (m_grid->cell(col, row)->colspan > 1)
+ {
+ int max_total_width = m_grid->column(col).max_width;
+ int min_total_width = m_grid->column(col).min_width;
+ for (int col2 = col + 1; col2 < col + m_grid->cell(col, row)->colspan; col2++)
+ {
+ max_total_width += m_grid->column(col2).max_width;
+ min_total_width += m_grid->column(col2).min_width;
+ }
+ if (min_total_width < m_grid->cell(col, row)->min_width)
+ {
+ m_grid->distribute_min_width(m_grid->cell(col, row)->min_width - min_total_width, col, col + m_grid->cell(col, row)->colspan - 1);
+ }
+ if (max_total_width < m_grid->cell(col, row)->max_width)
+ {
+ m_grid->distribute_max_width(m_grid->cell(col, row)->max_width - max_total_width, col, col + m_grid->cell(col, row)->colspan - 1);
+ }
+ }
+ }
+ }
+
+ // If the 'table' or 'inline-table' element's 'width' property has a computed value (W) other than 'auto', the used width is the
+ // greater of W, CAPMIN, and the minimum width required by all the columns plus cell spacing or borders (MIN).
+ // If the used width is greater than MIN, the extra width should be distributed over the columns.
+ //
+ // If the 'table' or 'inline-table' element has 'width: auto', the used width is the greater of the table's containing block width,
+ // CAPMIN, and MIN. However, if either CAPMIN or the maximum width required by the columns plus cell spacing or borders (MAX) is
+ // less than that of the containing block, use max(MAX, CAPMIN).
+
+
+ int table_width = 0;
+ int min_table_width = 0;
+ int max_table_width = 0;
+
+ if (self_size.width.type == containing_block_context::cbc_value_type_absolute)
+ {
+ table_width = m_grid->calc_table_width(self_size.render_width - table_width_spacing, false, min_table_width, max_table_width);
+ }
+ else
+ {
+ 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);
+ }
+
+ min_table_width += table_width_spacing;
+ max_table_width += table_width_spacing;
+ table_width += table_width_spacing;
+ m_grid->calc_horizontal_positions(m_borders, src_el()->css().get_border_collapse(), m_border_spacing_x);
+
+ bool row_span_found = false;
+
+ // render cells with computed width
+ for (int row = 0; row < m_grid->rows_count(); row++)
+ {
+ m_grid->row(row).height = 0;
+ for (int col = 0; col < m_grid->cols_count(); col++)
+ {
+ table_cell* cell = m_grid->cell(col, row);
+ if (cell->el)
+ {
+ int span_col = col + cell->colspan - 1;
+ if (span_col >= m_grid->cols_count())
+ {
+ span_col = m_grid->cols_count() - 1;
+ }
+ int cell_width = m_grid->column(span_col).right - m_grid->column(col).left;
+
+ //if (cell->el->pos().width != cell_width - cell->el->content_offset_left() -
+ // cell->el->content_offset_right())
+ {
+ cell->el->render(m_grid->column(col).left, 0, self_size.new_width(cell_width), fmt_ctx, true);
+ cell->el->pos().width = cell_width - cell->el->content_offset_left() -
+ cell->el->content_offset_right();
+ }
+ /*else
+ {
+ cell->el->pos().x = m_grid->column(col).left + cell->el->content_offset_left();
+ }*/
+
+ if (cell->rowspan <= 1)
+ {
+ m_grid->row(row).height = std::max(m_grid->row(row).height, cell->el->height());
+ }
+ else
+ {
+ row_span_found = true;
+ }
+
+ }
+ }
+ }
+
+ if (row_span_found)
+ {
+ for (int col = 0; col < m_grid->cols_count(); col++)
+ {
+ for (int row = 0; row < m_grid->rows_count(); row++)
+ {
+ table_cell* cell = m_grid->cell(col, row);
+ if (cell->el)
+ {
+ int span_row = row + cell->rowspan - 1;
+ if (span_row >= m_grid->rows_count())
+ {
+ span_row = m_grid->rows_count() - 1;
+ }
+ if (span_row != row)
+ {
+ int h = 0;
+ for (int i = row; i <= span_row; i++)
+ {
+ h += m_grid->row(i).height;
+ }
+ if (h < cell->el->height())
+ {
+ m_grid->row(span_row).height += cell->el->height() - h;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Calculate vertical table spacing
+ int table_height_spacing = 0;
+ if (src_el()->css().get_border_collapse() == border_collapse_separate)
+ {
+ table_height_spacing = m_border_spacing_y * (m_grid->rows_count() + 1);
+ }
+ else
+ {
+ table_height_spacing = 0;
+
+ if (m_grid->rows_count())
+ {
+ table_height_spacing -= std::min(border_top(), m_grid->row(0).border_top);
+ table_height_spacing -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom);
+ }
+
+ for (int row = 1; row < m_grid->rows_count(); row++)
+ {
+ table_height_spacing -= std::min(m_grid->row(row).border_top, m_grid->row(row - 1).border_bottom);
+ }
+ }
+
+
+ // calculate block height
+ int block_height = 0;
+ if(self_size.height.type != containing_block_context::cbc_value_type_auto && self_size.height > 0)
+ {
+ block_height = self_size.height - (m_padding.height() + m_borders.height());
+ }
+
+ // calculate minimum height from m_css.get_min_height()
+ int min_height = 0;
+ if (!src_el()->css().get_min_height().is_predefined() && src_el()->css().get_min_height().units() == css_units_percentage)
+ {
+ min_height = src_el()->css().get_min_height().calc_percent(containing_block_size.height);
+ }
+ else
+ {
+ min_height = (int)src_el()->css().get_min_height().val();
+ }
+
+ int minimum_table_height = std::max(block_height, min_height);
+
+ m_grid->calc_rows_height(minimum_table_height - table_height_spacing, m_border_spacing_y);
+ m_grid->calc_vertical_positions(m_borders, src_el()->css().get_border_collapse(), m_border_spacing_y);
+
+ int table_height = 0;
+
+ // place cells vertically
+ for (int col = 0; col < m_grid->cols_count(); col++)
+ {
+ for (int row = 0; row < m_grid->rows_count(); row++)
+ {
+ table_cell* cell = m_grid->cell(col, row);
+ if (cell->el)
+ {
+ int span_row = row + cell->rowspan - 1;
+ if (span_row >= m_grid->rows_count())
+ {
+ span_row = m_grid->rows_count() - 1;
+ }
+ cell->el->pos().y = m_grid->row(row).top + cell->el->content_offset_top();
+ cell->el->pos().height = m_grid->row(span_row).bottom - m_grid->row(row).top -
+ cell->el->content_offset_top() -
+ cell->el->content_offset_bottom();
+ table_height = std::max(table_height, m_grid->row(span_row).bottom);
+ cell->el->apply_vertical_align();
+ }
+ }
+ }
+
+ if (src_el()->css().get_border_collapse() == border_collapse_collapse)
+ {
+ if (m_grid->rows_count())
+ {
+ table_height -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom);
+ }
+ }
+ else
+ {
+ table_height += m_border_spacing_y;
+ }
+
+ // Render table captions
+ // Table border doesn't round the caption, so we have to start caption in the border position
+ int top_captions = -border_top();
+
+ for (auto& caption : m_grid->captions())
+ {
+ if(caption->css().get_caption_side() == caption_side_top)
+ {
+ caption->render(-border_left(), top_captions, self_size.new_width(table_width + border_left() + border_right()), fmt_ctx);
+ top_captions += caption->height();
+ }
+ }
+
+ if (top_captions)
+ {
+ // Add border height to get the top of cells
+ top_captions += border_top();
+
+ // Save caption height for draw_background
+ m_grid->top_captions_height(top_captions);
+
+ // Move table cells to the bottom side
+ for (int row = 0; row < m_grid->rows_count(); row++)
+ {
+ m_grid->row(row).el_row->pos().y += top_captions;
+ for (int col = 0; col < m_grid->cols_count(); col++)
+ {
+ table_cell* cell = m_grid->cell(col, row);
+ if (cell->el)
+ {
+ cell->el->pos().y += top_captions;
+ }
+ }
+ }
+ }
+
+ int bottom_captions = 0;
+
+ for (auto& caption : m_grid->captions())
+ {
+ if(caption->css().get_caption_side() == caption_side_bottom)
+ {
+ caption->render(-border_left(), table_height + top_captions + bottom_captions, self_size.new_width(table_width + border_left() + border_right()), fmt_ctx);
+ bottom_captions += caption->height();
+ }
+ }
+
+ m_pos.move_to(x + content_offset_left(), y + content_offset_top());
+ m_pos.width = table_width;
+ m_pos.height = table_height + top_captions + bottom_captions;
+
+ if(self_size.width.type != containing_block_context::cbc_value_type_absolute)
+ {
+ return std::min(table_width, max_table_width) + content_offset_width();
+ }
+ return table_width + content_offset_width();
+}
+
+std::shared_ptr<litehtml::render_item> litehtml::render_item_table::init()
+{
+ // Initialize Grid
+ m_grid = std::unique_ptr<table_grid>(new table_grid());
+
+ go_inside_table table_selector;
+ table_rows_selector row_selector;
+ table_cells_selector cell_selector;
+
+ elements_iterator row_iter(false, &table_selector, &row_selector);
+
+ row_iter.process(shared_from_this(), [&](std::shared_ptr<render_item>& el, iterator_item_type /*item_type*/)
+ {
+ m_grid->begin_row(el);
+
+
+ elements_iterator cell_iter(true, &table_selector, &cell_selector);
+ cell_iter.process(el, [&](std::shared_ptr<render_item>& el, iterator_item_type item_type)
+ {
+ if(item_type != iterator_item_type_end_parent)
+ {
+ el = el->init();
+ m_grid->add_cell(el);
+ }
+ });
+ });
+
+ for (auto& el : m_children)
+ {
+ if (el->src_el()->css().get_display() == display_table_caption)
+ {
+ el = el->init();
+ m_grid->captions().push_back(el);
+ }
+ }
+
+ m_grid->finish();
+
+ if(src_el()->css().get_border_collapse() == border_collapse_separate)
+ {
+ int font_size = src_el()->css().get_font_size();
+ document::ptr doc = src_el()->get_document();
+ m_border_spacing_x = doc->to_pixels(src_el()->css().get_border_spacing_x(), font_size);
+ m_border_spacing_y = doc->to_pixels(src_el()->css().get_border_spacing_y(), font_size);
+ } else
+ {
+ m_border_spacing_x = 0;
+ m_border_spacing_y = 0;
+ }
+
+ src_el()->add_render(shared_from_this());
+
+ return shared_from_this();
+}
+
+void litehtml::render_item_table::draw_children(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex)
+{
+ if (!m_grid) return;
+
+ position pos = m_pos;
+ pos.x += x;
+ pos.y += y;
+ for (auto& caption : m_grid->captions())
+ {
+ if (flag == draw_block)
+ {
+ caption->src_el()->draw(hdc, pos.x, pos.y, clip, caption);
+ }
+ caption->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
+ }
+ for (int row = 0; row < m_grid->rows_count(); row++)
+ {
+ if (flag == draw_block)
+ {
+ m_grid->row(row).el_row->src_el()->draw_background(hdc, pos.x, pos.y, clip, m_grid->row(row).el_row);
+ }
+ for (int col = 0; col < m_grid->cols_count(); col++)
+ {
+ table_cell* cell = m_grid->cell(col, row);
+ if (cell->el)
+ {
+ if (flag == draw_block)
+ {
+ cell->el->src_el()->draw(hdc, pos.x, pos.y, clip, cell->el);
+ }
+ cell->el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
+ }
+ }
+ }
+}
+
+int litehtml::render_item_table::get_draw_vertical_offset()
+{
+ if(m_grid)
+ {
+ return m_grid->top_captions_height();
+ }
+ return 0;
+}
+
+void litehtml::render_item_table_row::get_inline_boxes( position::vector& boxes ) const
+{
+ position pos;
+ for(auto& el : m_children)
+ {
+ if(el->src_el()->css().get_display() == display_table_cell)
+ {
+ pos.x = el->left() + el->margin_left();
+ pos.y = el->top() - m_padding.top - m_borders.top;
+
+ pos.width = el->right() - pos.x - el->margin_right() - el->margin_left();
+ pos.height = el->height() + m_padding.top + m_padding.bottom + m_borders.top + m_borders.bottom;
+
+ boxes.push_back(pos);
+ }
+ }
+}