5 #include "stylesheet.h"
9 #include "el_before_after.h"
11 litehtml::html_tag::html_tag(const std::shared_ptr<litehtml::document>& doc) : litehtml::element(doc)
13 m_box_sizing = box_sizing_content_box;
15 m_overflow = overflow_visible;
17 m_text_align = text_align_left;
18 m_el_position = element_position_static;
19 m_display = display_inline;
20 m_vertical_align = va_baseline;
21 m_list_style_type = list_style_type_none;
22 m_list_style_position = list_style_position_outside;
27 m_white_space = white_space_normal;
28 m_lh_predefined = false;
30 m_visibility = visibility_visible;
31 m_border_spacing_x = 0;
32 m_border_spacing_y = 0;
33 m_border_collapse = border_collapse_separate;
36 litehtml::html_tag::~html_tag()
41 bool litehtml::html_tag::appendChild(const element::ptr &el)
45 el->parent(shared_from_this());
46 m_children.push_back(el);
52 bool litehtml::html_tag::removeChild(const element::ptr &el)
54 if(el && el->parent() == shared_from_this())
57 m_children.erase(std::remove(m_children.begin(), m_children.end(), el), m_children.end());
63 void litehtml::html_tag::clearRecursive()
65 for(auto& el : m_children)
74 const litehtml::tchar_t* litehtml::html_tag::get_tagName() const
79 void litehtml::html_tag::set_attr( const tchar_t* name, const tchar_t* val )
84 std::locale lc = std::locale::global(std::locale(""));
85 for(size_t i = 0; i < s_val.length(); i++)
87 s_val[i] = std::tolower(s_val[i], lc);
91 if( t_strcasecmp( name, _t("class") ) == 0 )
93 m_class_values.resize( 0 );
94 split_string( val, m_class_values, _t(" ") );
99 const litehtml::tchar_t* litehtml::html_tag::get_attr( const tchar_t* name, const tchar_t* def )
101 string_map::const_iterator attr = m_attrs.find(name);
102 if(attr != m_attrs.end())
104 return attr->second.c_str();
109 litehtml::elements_vector litehtml::html_tag::select_all( const tstring& selector )
111 css_selector sel(media_query_list::ptr(0));
114 return select_all(sel);
117 litehtml::elements_vector litehtml::html_tag::select_all( const css_selector& selector )
119 litehtml::elements_vector res;
120 select_all(selector, res);
124 void litehtml::html_tag::select_all(const css_selector& selector, elements_vector& res)
128 res.push_back(shared_from_this());
131 for(auto& el : m_children)
133 el->select_all(selector, res);
138 litehtml::element::ptr litehtml::html_tag::select_one( const tstring& selector )
140 css_selector sel(media_query_list::ptr(0));
143 return select_one(sel);
146 litehtml::element::ptr litehtml::html_tag::select_one( const css_selector& selector )
150 return shared_from_this();
153 for(auto& el : m_children)
155 element::ptr res = el->select_one(selector);
164 void litehtml::html_tag::apply_stylesheet( const litehtml::css& stylesheet )
166 remove_before_after();
168 for(const auto& sel : stylesheet.selectors())
170 int apply = select(*sel, false);
172 if(apply != select_no_match)
174 used_selector::ptr us = std::unique_ptr<used_selector>(new used_selector(sel, false));
176 if(sel->is_media_valid())
178 if(apply & select_match_pseudo_class)
180 if(select(*sel, true))
182 if(apply & select_match_with_after)
184 element::ptr el = get_element_after();
187 el->add_style(*sel->m_style);
189 } else if(apply & select_match_with_before)
191 element::ptr el = get_element_before();
194 el->add_style(*sel->m_style);
199 add_style(*sel->m_style);
203 } else if(apply & select_match_with_after)
205 element::ptr el = get_element_after();
208 el->add_style(*sel->m_style);
210 } else if(apply & select_match_with_before)
212 element::ptr el = get_element_before();
215 el->add_style(*sel->m_style);
219 add_style(*sel->m_style);
223 m_used_styles.push_back(std::move(us));
227 for(auto& el : m_children)
229 if(el->get_display() != display_inline_text)
231 el->apply_stylesheet(stylesheet);
236 void litehtml::html_tag::get_content_size( size& sz, int max_width )
239 if(m_display == display_block)
241 sz.width = max_width;
248 void litehtml::html_tag::draw( uint_ptr hdc, int x, int y, const position* clip )
250 position pos = m_pos;
254 draw_background(hdc, x, y, clip);
256 if(m_display == display_list_item && m_list_style_type != list_style_type_none)
258 if(m_overflow > overflow_visible)
260 position border_box = pos;
261 border_box += m_padding;
262 border_box += m_borders;
264 border_radiuses bdr_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);
266 bdr_radius -= m_borders;
267 bdr_radius -= m_padding;
269 get_document()->container()->set_clip(pos, bdr_radius, true, true);
272 draw_list_marker(hdc, pos);
274 if(m_overflow > overflow_visible)
276 get_document()->container()->del_clip();
281 litehtml::uint_ptr litehtml::html_tag::get_font(font_metrics* fm)
285 *fm = m_font_metrics;
290 const litehtml::tchar_t* litehtml::html_tag::get_style_property( const tchar_t* name, bool inherited, const tchar_t* def /*= 0*/ )
292 const tchar_t* ret = m_style.get_property(name);
293 element::ptr el_parent = parent();
296 if ( ( ret && !t_strcasecmp(ret, _t("inherit")) ) || (!ret && inherited) )
298 ret = el_parent->get_style_property(name, inherited, def);
310 void litehtml::html_tag::parse_styles(bool is_reparse)
312 const tchar_t* style = get_attr(_t("style"));
316 m_style.add(style, NULL);
320 document::ptr doc = get_document();
322 m_el_position = (element_position) value_index(get_style_property(_t("position"), false, _t("static")), element_position_strings, element_position_fixed);
323 m_text_align = (text_align) value_index(get_style_property(_t("text-align"), true, _t("left")), text_align_strings, text_align_left);
324 m_overflow = (overflow) value_index(get_style_property(_t("overflow"), false, _t("visible")), overflow_strings, overflow_visible);
325 m_white_space = (white_space) value_index(get_style_property(_t("white-space"), true, _t("normal")), white_space_strings, white_space_normal);
326 m_display = (style_display) value_index(get_style_property(_t("display"), false, _t("inline")), style_display_strings, display_inline);
327 m_visibility = (visibility) value_index(get_style_property(_t("visibility"), true, _t("visible")), visibility_strings, visibility_visible);
328 m_box_sizing = (box_sizing) value_index(get_style_property(_t("box-sizing"), false, _t("content-box")), box_sizing_strings, box_sizing_content_box);
330 if(m_el_position != element_position_static)
332 const tchar_t* val = get_style_property(_t("z-index"), false, 0);
335 m_z_index = t_atoi(val);
339 const tchar_t* va = get_style_property(_t("vertical-align"), true, _t("baseline"));
340 m_vertical_align = (vertical_align) value_index(va, vertical_align_strings, va_baseline);
342 const tchar_t* fl = get_style_property(_t("float"), false, _t("none"));
343 m_float = (element_float) value_index(fl, element_float_strings, float_none);
345 m_clear = (element_clear) value_index(get_style_property(_t("clear"), false, _t("none")), element_clear_strings, clear_none);
347 if (m_float != float_none)
349 // reset display in to block for floating elements
350 if (m_display != display_none)
352 m_display = display_block;
355 else if (m_display == display_table ||
356 m_display == display_table_caption ||
357 m_display == display_table_cell ||
358 m_display == display_table_column ||
359 m_display == display_table_column_group ||
360 m_display == display_table_footer_group ||
361 m_display == display_table_header_group ||
362 m_display == display_table_row ||
363 m_display == display_table_row_group)
365 doc->add_tabular(shared_from_this());
367 // fix inline boxes with absolute/fixed positions
368 else if (m_display != display_none && is_inline_box())
370 if (m_el_position == element_position_absolute || m_el_position == element_position_fixed)
372 m_display = display_block;
376 m_css_text_indent.fromString( get_style_property(_t("text-indent"), true, _t("0")), _t("0"));
378 m_css_width.fromString( get_style_property(_t("width"), false, _t("auto")), _t("auto"));
379 m_css_height.fromString( get_style_property(_t("height"), false, _t("auto")), _t("auto"));
381 doc->cvt_units(m_css_width, m_font_size);
382 doc->cvt_units(m_css_height, m_font_size);
384 m_css_min_width.fromString( get_style_property(_t("min-width"), false, _t("0")));
385 m_css_min_height.fromString( get_style_property(_t("min-height"), false, _t("0")));
387 m_css_max_width.fromString( get_style_property(_t("max-width"), false, _t("none")), _t("none"));
388 m_css_max_height.fromString( get_style_property(_t("max-height"), false, _t("none")), _t("none"));
390 doc->cvt_units(m_css_min_width, m_font_size);
391 doc->cvt_units(m_css_min_height, m_font_size);
393 m_css_offsets.left.fromString( get_style_property(_t("left"), false, _t("auto")), _t("auto"));
394 m_css_offsets.right.fromString( get_style_property(_t("right"), false, _t("auto")), _t("auto"));
395 m_css_offsets.top.fromString( get_style_property(_t("top"), false, _t("auto")), _t("auto"));
396 m_css_offsets.bottom.fromString( get_style_property(_t("bottom"), false, _t("auto")), _t("auto"));
398 doc->cvt_units(m_css_offsets.left, m_font_size);
399 doc->cvt_units(m_css_offsets.right, m_font_size);
400 doc->cvt_units(m_css_offsets.top, m_font_size);
401 doc->cvt_units(m_css_offsets.bottom, m_font_size);
403 m_css_margins.left.fromString( get_style_property(_t("margin-left"), false, _t("0")), _t("auto"));
404 m_css_margins.right.fromString( get_style_property(_t("margin-right"), false, _t("0")), _t("auto"));
405 m_css_margins.top.fromString( get_style_property(_t("margin-top"), false, _t("0")), _t("auto"));
406 m_css_margins.bottom.fromString( get_style_property(_t("margin-bottom"), false, _t("0")), _t("auto"));
408 m_css_padding.left.fromString( get_style_property(_t("padding-left"), false, _t("0")), _t(""));
409 m_css_padding.right.fromString( get_style_property(_t("padding-right"), false, _t("0")), _t(""));
410 m_css_padding.top.fromString( get_style_property(_t("padding-top"), false, _t("0")), _t(""));
411 m_css_padding.bottom.fromString( get_style_property(_t("padding-bottom"), false, _t("0")), _t(""));
413 m_css_borders.left.width.fromString( get_style_property(_t("border-left-width"), false, _t("medium")), border_width_strings);
414 m_css_borders.right.width.fromString( get_style_property(_t("border-right-width"), false, _t("medium")), border_width_strings);
415 m_css_borders.top.width.fromString( get_style_property(_t("border-top-width"), false, _t("medium")), border_width_strings);
416 m_css_borders.bottom.width.fromString( get_style_property(_t("border-bottom-width"), false, _t("medium")), border_width_strings);
418 m_css_borders.left.color = web_color::from_string(get_style_property(_t("border-left-color"), false, _t("")), doc->container());
419 m_css_borders.left.style = (border_style) value_index(get_style_property(_t("border-left-style"), false, _t("none")), border_style_strings, border_style_none);
421 m_css_borders.right.color = web_color::from_string(get_style_property(_t("border-right-color"), false, _t("")), doc->container());
422 m_css_borders.right.style = (border_style) value_index(get_style_property(_t("border-right-style"), false, _t("none")), border_style_strings, border_style_none);
424 m_css_borders.top.color = web_color::from_string(get_style_property(_t("border-top-color"), false, _t("")), doc->container());
425 m_css_borders.top.style = (border_style) value_index(get_style_property(_t("border-top-style"), false, _t("none")), border_style_strings, border_style_none);
427 m_css_borders.bottom.color = web_color::from_string(get_style_property(_t("border-bottom-color"), false, _t("")), doc->container());
428 m_css_borders.bottom.style = (border_style) value_index(get_style_property(_t("border-bottom-style"), false, _t("none")), border_style_strings, border_style_none);
430 m_css_borders.radius.top_left_x.fromString(get_style_property(_t("border-top-left-radius-x"), false, _t("0")));
431 m_css_borders.radius.top_left_y.fromString(get_style_property(_t("border-top-left-radius-y"), false, _t("0")));
433 m_css_borders.radius.top_right_x.fromString(get_style_property(_t("border-top-right-radius-x"), false, _t("0")));
434 m_css_borders.radius.top_right_y.fromString(get_style_property(_t("border-top-right-radius-y"), false, _t("0")));
436 m_css_borders.radius.bottom_right_x.fromString(get_style_property(_t("border-bottom-right-radius-x"), false, _t("0")));
437 m_css_borders.radius.bottom_right_y.fromString(get_style_property(_t("border-bottom-right-radius-y"), false, _t("0")));
439 m_css_borders.radius.bottom_left_x.fromString(get_style_property(_t("border-bottom-left-radius-x"), false, _t("0")));
440 m_css_borders.radius.bottom_left_y.fromString(get_style_property(_t("border-bottom-left-radius-y"), false, _t("0")));
442 doc->cvt_units(m_css_borders.radius.bottom_left_x, m_font_size);
443 doc->cvt_units(m_css_borders.radius.bottom_left_y, m_font_size);
444 doc->cvt_units(m_css_borders.radius.bottom_right_x, m_font_size);
445 doc->cvt_units(m_css_borders.radius.bottom_right_y, m_font_size);
446 doc->cvt_units(m_css_borders.radius.top_left_x, m_font_size);
447 doc->cvt_units(m_css_borders.radius.top_left_y, m_font_size);
448 doc->cvt_units(m_css_borders.radius.top_right_x, m_font_size);
449 doc->cvt_units(m_css_borders.radius.top_right_y, m_font_size);
451 doc->cvt_units(m_css_text_indent, m_font_size);
453 m_margins.left = doc->cvt_units(m_css_margins.left, m_font_size);
454 m_margins.right = doc->cvt_units(m_css_margins.right, m_font_size);
455 m_margins.top = doc->cvt_units(m_css_margins.top, m_font_size);
456 m_margins.bottom = doc->cvt_units(m_css_margins.bottom, m_font_size);
458 m_padding.left = doc->cvt_units(m_css_padding.left, m_font_size);
459 m_padding.right = doc->cvt_units(m_css_padding.right, m_font_size);
460 m_padding.top = doc->cvt_units(m_css_padding.top, m_font_size);
461 m_padding.bottom = doc->cvt_units(m_css_padding.bottom, m_font_size);
463 m_borders.left = doc->cvt_units(m_css_borders.left.width, m_font_size);
464 m_borders.right = doc->cvt_units(m_css_borders.right.width, m_font_size);
465 m_borders.top = doc->cvt_units(m_css_borders.top.width, m_font_size);
466 m_borders.bottom = doc->cvt_units(m_css_borders.bottom.width, m_font_size);
468 css_length line_height;
469 line_height.fromString(get_style_property(_t("line-height"), true, _t("normal")), _t("normal"));
470 if(line_height.is_predefined())
472 m_line_height = m_font_metrics.height;
473 m_lh_predefined = true;
474 } else if(line_height.units() == css_units_none)
476 m_line_height = (int) (line_height.val() * m_font_size);
477 m_lh_predefined = false;
480 m_line_height = doc->cvt_units(line_height, m_font_size, m_font_size);
481 m_lh_predefined = false;
485 if(m_display == display_list_item)
487 const tchar_t* list_type = get_style_property(_t("list-style-type"), true, _t("disc"));
488 m_list_style_type = (list_style_type) value_index(list_type, list_style_type_strings, list_style_type_disc);
490 const tchar_t* list_pos = get_style_property(_t("list-style-position"), true, _t("outside"));
491 m_list_style_position = (list_style_position) value_index(list_pos, list_style_position_strings, list_style_position_outside);
493 const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0);
494 if(list_image && list_image[0])
497 css::parse_css_url(list_image, url);
499 const tchar_t* list_image_baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0);
500 doc->container()->load_image(url.c_str(), list_image_baseurl, true);
509 for(auto& el : m_children)
516 int litehtml::html_tag::render( int x, int y, int max_width, bool second_pass )
518 if (m_display == display_table || m_display == display_inline_table)
520 return render_table(x, y, max_width, second_pass);
523 return render_box(x, y, max_width, second_pass);
526 bool litehtml::html_tag::is_white_space() const
531 int litehtml::html_tag::get_font_size() const
536 int litehtml::html_tag::get_base_line()
545 bl = m_boxes.back()->baseline() + content_margins_bottom();
550 void litehtml::html_tag::init()
552 if (m_display == display_table || m_display == display_inline_table)
560 m_grid = std::unique_ptr<table_grid>(new table_grid());
563 go_inside_table table_selector;
564 table_rows_selector row_selector;
565 table_cells_selector cell_selector;
567 elements_iterator row_iter(shared_from_this(), &table_selector, &row_selector);
569 element::ptr row = row_iter.next(false);
572 m_grid->begin_row(row);
574 elements_iterator cell_iter(row, &table_selector, &cell_selector);
575 element::ptr cell = cell_iter.next();
578 m_grid->add_cell(cell);
580 cell = cell_iter.next(false);
582 row = row_iter.next(false);
588 for (auto& el : m_children)
594 int litehtml::html_tag::select(const css_selector& selector, bool apply_pseudo)
596 int right_res = select(selector.m_right, apply_pseudo);
597 if(right_res == select_no_match)
599 return select_no_match;
601 element::ptr el_parent = parent();
606 return select_no_match;
608 switch(selector.m_combinator)
610 case combinator_descendant:
612 bool is_pseudo = false;
613 element::ptr res = find_ancestor(*selector.m_left, apply_pseudo, &is_pseudo);
616 return select_no_match;
621 right_res |= select_match_pseudo_class;
626 case combinator_child:
628 int res = el_parent->select(*selector.m_left, apply_pseudo);
629 if(res == select_no_match)
631 return select_no_match;
634 if(right_res != select_match_pseudo_class)
641 case combinator_adjacent_sibling:
643 bool is_pseudo = false;
644 element::ptr res = el_parent->find_adjacent_sibling(shared_from_this(), *selector.m_left, apply_pseudo, &is_pseudo);
647 return select_no_match;
652 right_res |= select_match_pseudo_class;
657 case combinator_general_sibling:
659 bool is_pseudo = false;
660 element::ptr res = el_parent->find_sibling(shared_from_this(), *selector.m_left, apply_pseudo, &is_pseudo);
663 return select_no_match;
668 right_res |= select_match_pseudo_class;
674 right_res = select_no_match;
680 int litehtml::html_tag::select(const css_element_selector& selector, bool apply_pseudo)
682 if(!selector.m_tag.empty() && selector.m_tag != _t("*"))
684 if(selector.m_tag != m_tag)
686 return select_no_match;
690 int res = select_match;
691 element::ptr el_parent = parent();
693 for(css_attribute_selector::vector::const_iterator i = selector.m_attrs.begin(); i != selector.m_attrs.end(); i++)
695 const tchar_t* attr_value = get_attr(i->attribute.c_str());
701 return select_no_match;
707 return select_no_match;
710 if(i->attribute == _t("class"))
712 const string_vector & tokens1 = m_class_values;
713 const string_vector & tokens2 = i->class_val;
715 for(string_vector::const_iterator str1 = tokens2.begin(); str1 != tokens2.end() && found; str1++)
718 for(string_vector::const_iterator str2 = tokens1.begin(); str2 != tokens1.end() && !f; str2++)
720 if( !t_strcasecmp(str1->c_str(), str2->c_str()) )
732 return select_no_match;
736 if( t_strcasecmp(i->val.c_str(), attr_value) )
738 return select_no_match;
743 case select_contain_str:
746 return select_no_match;
747 } else if(!t_strstr(attr_value, i->val.c_str()))
749 return select_no_match;
752 case select_start_str:
755 return select_no_match;
756 } else if(t_strncmp(attr_value, i->val.c_str(), i->val.length()))
758 return select_no_match;
764 return select_no_match;
765 } else if(t_strncmp(attr_value, i->val.c_str(), i->val.length()))
767 const tchar_t* s = attr_value + t_strlen(attr_value) - i->val.length() - 1;
770 return select_no_match;
774 return select_no_match;
778 case select_pseudo_element:
779 if(i->val == _t("after"))
781 res |= select_match_with_after;
782 } else if(i->val == _t("before"))
784 res |= select_match_with_before;
787 return select_no_match;
790 case select_pseudo_class:
793 if (!el_parent) return select_no_match;
795 tstring selector_param;
796 tstring selector_name;
798 tstring::size_type begin = i->val.find_first_of(_t('('));
799 tstring::size_type end = (begin == tstring::npos) ? tstring::npos : find_close_bracket(i->val, begin);
800 if(begin != tstring::npos && end != tstring::npos)
802 selector_param = i->val.substr(begin + 1, end - begin - 1);
804 if(begin != tstring::npos)
806 selector_name = i->val.substr(0, begin);
807 litehtml::trim(selector_name);
810 selector_name = i->val;
813 int selector = value_index(selector_name.c_str(), pseudo_class_strings);
817 case pseudo_class_only_child:
818 if (!el_parent->is_only_child(shared_from_this(), false))
820 return select_no_match;
823 case pseudo_class_only_of_type:
824 if (!el_parent->is_only_child(shared_from_this(), true))
826 return select_no_match;
829 case pseudo_class_first_child:
830 if (!el_parent->is_nth_child(shared_from_this(), 0, 1, false))
832 return select_no_match;
835 case pseudo_class_first_of_type:
836 if (!el_parent->is_nth_child(shared_from_this(), 0, 1, true))
838 return select_no_match;
841 case pseudo_class_last_child:
842 if (!el_parent->is_nth_last_child(shared_from_this(), 0, 1, false))
844 return select_no_match;
847 case pseudo_class_last_of_type:
848 if (!el_parent->is_nth_last_child(shared_from_this(), 0, 1, true))
850 return select_no_match;
853 case pseudo_class_nth_child:
854 case pseudo_class_nth_of_type:
855 case pseudo_class_nth_last_child:
856 case pseudo_class_nth_last_of_type:
858 if(selector_param.empty()) return select_no_match;
863 parse_nth_child_params(selector_param, num, off);
864 if(!num && !off) return select_no_match;
867 case pseudo_class_nth_child:
868 if (!el_parent->is_nth_child(shared_from_this(), num, off, false))
870 return select_no_match;
873 case pseudo_class_nth_of_type:
874 if (!el_parent->is_nth_child(shared_from_this(), num, off, true))
876 return select_no_match;
879 case pseudo_class_nth_last_child:
880 if (!el_parent->is_nth_last_child(shared_from_this(), num, off, false))
882 return select_no_match;
885 case pseudo_class_nth_last_of_type:
886 if (!el_parent->is_nth_last_child(shared_from_this(), num, off, true))
888 return select_no_match;
895 case pseudo_class_not:
897 css_element_selector sel;
898 sel.parse(selector_param);
899 if(select(sel, apply_pseudo))
901 return select_no_match;
905 case pseudo_class_lang:
907 trim( selector_param );
909 if( !get_document()->match_lang( selector_param ) )
911 return select_no_match;
916 if(std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), i->val) == m_pseudo_classes.end())
918 return select_no_match;
924 res |= select_match_pseudo_class;
932 litehtml::element::ptr litehtml::html_tag::find_ancestor(const css_selector& selector, bool apply_pseudo, bool* is_pseudo)
934 element::ptr el_parent = parent();
939 int res = el_parent->select(selector, apply_pseudo);
940 if(res != select_no_match)
944 if(res & select_match_pseudo_class)
954 return el_parent->find_ancestor(selector, apply_pseudo, is_pseudo);
957 int litehtml::html_tag::get_floats_height(element_float el_float) const
959 if(is_floats_holder())
963 bool process = false;
965 for(const auto& fb : m_floats_left)
974 if (fb.clear_floats == clear_left || fb.clear_floats == clear_both)
980 if (fb.clear_floats == clear_right || fb.clear_floats == clear_both)
988 if(el_float == float_none)
990 h = std::max(h, fb.pos.bottom());
993 h = std::max(h, fb.pos.top());
999 for(const auto fb : m_floats_right)
1008 if (fb.clear_floats == clear_left || fb.clear_floats == clear_both)
1014 if (fb.clear_floats == clear_right || fb.clear_floats == clear_both)
1022 if(el_float == float_none)
1024 h = std::max(h, fb.pos.bottom());
1027 h = std::max(h, fb.pos.top());
1034 element::ptr el_parent = parent();
1037 int h = el_parent->get_floats_height(el_float);
1043 int litehtml::html_tag::get_left_floats_height() const
1045 if(is_floats_holder())
1048 if(!m_floats_left.empty())
1050 for (const auto& fb : m_floats_left)
1052 h = std::max(h, fb.pos.bottom());
1057 element::ptr el_parent = parent();
1060 int h = el_parent->get_left_floats_height();
1066 int litehtml::html_tag::get_right_floats_height() const
1068 if(is_floats_holder())
1071 if(!m_floats_right.empty())
1073 for(const auto& fb : m_floats_right)
1075 h = std::max(h, fb.pos.bottom());
1080 element::ptr el_parent = parent();
1083 int h = el_parent->get_right_floats_height();
1089 int litehtml::html_tag::get_line_left( int y )
1091 if(is_floats_holder())
1093 if(m_cahe_line_left.is_valid && m_cahe_line_left.hash == y)
1095 return m_cahe_line_left.val;
1099 for(const auto& fb : m_floats_left)
1101 if (y >= fb.pos.top() && y < fb.pos.bottom())
1103 w = std::max(w, fb.pos.right());
1104 if (w < fb.pos.right())
1110 m_cahe_line_left.set_value(y, w);
1113 element::ptr el_parent = parent();
1116 int w = el_parent->get_line_left(y + m_pos.y);
1121 return w - (w ? m_pos.x : 0);
1126 int litehtml::html_tag::get_line_right( int y, int def_right )
1128 if(is_floats_holder())
1130 if(m_cahe_line_right.is_valid && m_cahe_line_right.hash == y)
1132 if(m_cahe_line_right.is_default)
1137 return std::min(m_cahe_line_right.val, def_right);
1142 m_cahe_line_right.is_default = true;
1143 for(const auto& fb : m_floats_right)
1145 if(y >= fb.pos.top() && y < fb.pos.bottom())
1147 w = std::min(w, fb.pos.left());
1148 m_cahe_line_right.is_default = false;
1149 if(w > fb.pos.left())
1155 m_cahe_line_right.set_value(y, w);
1158 element::ptr el_parent = parent();
1161 int w = el_parent->get_line_right(y + m_pos.y, def_right + m_pos.x);
1168 void litehtml::html_tag::get_line_left_right( int y, int def_right, int& ln_left, int& ln_right )
1170 if(is_floats_holder())
1172 ln_left = get_line_left(y);
1173 ln_right = get_line_right(y, def_right);
1176 element::ptr el_parent = parent();
1179 el_parent->get_line_left_right(y + m_pos.y, def_right + m_pos.x, ln_left, ln_right);
1181 ln_right -= m_pos.x;
1191 int litehtml::html_tag::fix_line_width( int max_width, element_float flt )
1194 if(!m_boxes.empty())
1196 elements_vector els;
1197 m_boxes.back()->get_elements(els);
1198 bool was_cleared = false;
1199 if(!els.empty() && els.front()->get_clear() != clear_none)
1201 if(els.front()->get_clear() == clear_both)
1206 if( (flt == float_left && els.front()->get_clear() == clear_left) ||
1207 (flt == float_right && els.front()->get_clear() == clear_right) )
1218 for(elements_vector::iterator i = els.begin(); i != els.end(); i++)
1220 int rw = place_element((*i), max_width);
1229 if(m_boxes.back()->get_type() == box_line)
1231 line_top = m_boxes.back()->top();
1234 line_top = m_boxes.back()->bottom();
1238 int line_right = max_width;
1239 get_line_left_right(line_top, max_width, line_left, line_right);
1241 if(m_boxes.back()->get_type() == box_line)
1243 if(m_boxes.size() == 1 && m_list_style_type != list_style_type_none && m_list_style_position == list_style_position_inside)
1245 int sz_font = get_font_size();
1246 line_left += sz_font;
1249 if(m_css_text_indent.val() != 0)
1251 bool line_box_found = false;
1252 for(box::vector::iterator iter = m_boxes.begin(); iter < m_boxes.end(); iter++)
1254 if((*iter)->get_type() == box_line)
1256 line_box_found = true;
1262 line_left += m_css_text_indent.calc_percent(max_width);
1268 elements_vector els;
1269 m_boxes.back()->new_width(line_left, line_right, els);
1272 int rw = place_element(el, max_width);
1284 void litehtml::html_tag::add_float(const element::ptr &el, int x, int y)
1286 if(is_floats_holder())
1289 fb.pos.x = el->left() + x;
1290 fb.pos.y = el->top() + y;
1291 fb.pos.width = el->width();
1292 fb.pos.height = el->height();
1293 fb.float_side = el->get_float();
1294 fb.clear_floats = el->get_clear();
1297 if(fb.float_side == float_left)
1299 if(m_floats_left.empty())
1301 m_floats_left.push_back(fb);
1304 bool inserted = false;
1305 for(floated_box::vector::iterator i = m_floats_left.begin(); i != m_floats_left.end(); i++)
1307 if(fb.pos.right() > i->pos.right())
1309 m_floats_left.insert(i, std::move(fb));
1316 m_floats_left.push_back(std::move(fb));
1319 m_cahe_line_left.invalidate();
1320 } else if(fb.float_side == float_right)
1322 if(m_floats_right.empty())
1324 m_floats_right.push_back(std::move(fb));
1327 bool inserted = false;
1328 for(floated_box::vector::iterator i = m_floats_right.begin(); i != m_floats_right.end(); i++)
1330 if(fb.pos.left() < i->pos.left())
1332 m_floats_right.insert(i, std::move(fb));
1339 m_floats_right.push_back(fb);
1342 m_cahe_line_right.invalidate();
1346 element::ptr el_parent = parent();
1349 el_parent->add_float(el, x + m_pos.x, y + m_pos.y);
1354 int litehtml::html_tag::find_next_line_top( int top, int width, int def_right )
1356 if(is_floats_holder())
1361 for(const auto& fb : m_floats_left)
1363 if(fb.pos.top() >= top)
1365 if(find(points.begin(), points.end(), fb.pos.top()) == points.end())
1367 points.push_back(fb.pos.top());
1370 if (fb.pos.bottom() >= top)
1372 if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end())
1374 points.push_back(fb.pos.bottom());
1379 for (const auto& fb : m_floats_right)
1381 if (fb.pos.top() >= top)
1383 if (find(points.begin(), points.end(), fb.pos.top()) == points.end())
1385 points.push_back(fb.pos.top());
1388 if (fb.pos.bottom() >= top)
1390 if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end())
1392 points.push_back(fb.pos.bottom());
1399 sort(points.begin(), points.end(), std::less<int>( ));
1400 new_top = points.back();
1402 for(auto pt : points)
1405 int pos_right = def_right;
1406 get_line_left_right(pt, def_right, pos_left, pos_right);
1408 if(pos_right - pos_left >= width)
1417 element::ptr el_parent = parent();
1420 int new_top = el_parent->find_next_line_top(top + m_pos.y, width, def_right + m_pos.x);
1421 return new_top - m_pos.y;
1426 void litehtml::html_tag::parse_background()
1428 // parse background-color
1429 m_bg.m_color = get_color(_t("background-color"), false, web_color(0, 0, 0, 0));
1431 // parse background-position
1432 const tchar_t* str = get_style_property(_t("background-position"), false, _t("0% 0%"));
1436 split_string(str, res, _t(" \t"));
1441 if( value_in_list(res[0].c_str(), _t("left;right;center")) )
1443 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1444 m_bg.m_position.y.set_value(50, css_units_percentage);
1445 } else if( value_in_list(res[0].c_str(), _t("top;bottom;center")) )
1447 m_bg.m_position.y.fromString(res[0], _t("top;bottom;center"));
1448 m_bg.m_position.x.set_value(50, css_units_percentage);
1451 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1452 m_bg.m_position.y.set_value(50, css_units_percentage);
1456 if(value_in_list(res[0].c_str(), _t("left;right")))
1458 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1459 m_bg.m_position.y.fromString(res[1], _t("top;bottom;center"));
1460 } else if(value_in_list(res[0].c_str(), _t("top;bottom")))
1462 m_bg.m_position.x.fromString(res[1], _t("left;right;center"));
1463 m_bg.m_position.y.fromString(res[0], _t("top;bottom;center"));
1464 } else if(value_in_list(res[1].c_str(), _t("left;right")))
1466 m_bg.m_position.x.fromString(res[1], _t("left;right;center"));
1467 m_bg.m_position.y.fromString(res[0], _t("top;bottom;center"));
1468 }else if(value_in_list(res[1].c_str(), _t("top;bottom")))
1470 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1471 m_bg.m_position.y.fromString(res[1], _t("top;bottom;center"));
1474 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1475 m_bg.m_position.y.fromString(res[1], _t("top;bottom;center"));
1479 if(m_bg.m_position.x.is_predefined())
1481 switch(m_bg.m_position.x.predef())
1484 m_bg.m_position.x.set_value(0, css_units_percentage);
1487 m_bg.m_position.x.set_value(100, css_units_percentage);
1490 m_bg.m_position.x.set_value(50, css_units_percentage);
1494 if(m_bg.m_position.y.is_predefined())
1496 switch(m_bg.m_position.y.predef())
1499 m_bg.m_position.y.set_value(0, css_units_percentage);
1502 m_bg.m_position.y.set_value(100, css_units_percentage);
1505 m_bg.m_position.y.set_value(50, css_units_percentage);
1511 m_bg.m_position.x.set_value(0, css_units_percentage);
1512 m_bg.m_position.y.set_value(0, css_units_percentage);
1516 m_bg.m_position.y.set_value(0, css_units_percentage);
1517 m_bg.m_position.x.set_value(0, css_units_percentage);
1520 str = get_style_property(_t("background-size"), false, _t("auto"));
1524 split_string(str, res, _t(" \t"));
1527 m_bg.m_position.width.fromString(res[0], background_size_strings);
1530 m_bg.m_position.height.fromString(res[1], background_size_strings);
1533 m_bg.m_position.height.predef(background_size_auto);
1537 m_bg.m_position.width.predef(background_size_auto);
1538 m_bg.m_position.height.predef(background_size_auto);
1542 document::ptr doc = get_document();
1544 doc->cvt_units(m_bg.m_position.x, m_font_size);
1545 doc->cvt_units(m_bg.m_position.y, m_font_size);
1546 doc->cvt_units(m_bg.m_position.width, m_font_size);
1547 doc->cvt_units(m_bg.m_position.height, m_font_size);
1549 // parse background_attachment
1550 m_bg.m_attachment = (background_attachment) value_index(
1551 get_style_property(_t("background-attachment"), false, _t("scroll")),
1552 background_attachment_strings,
1553 background_attachment_scroll);
1555 // parse background_attachment
1556 m_bg.m_repeat = (background_repeat) value_index(
1557 get_style_property(_t("background-repeat"), false, _t("repeat")),
1558 background_repeat_strings,
1559 background_repeat_repeat);
1561 // parse background_clip
1562 m_bg.m_clip = (background_box) value_index(
1563 get_style_property(_t("background-clip"), false, _t("border-box")),
1564 background_box_strings,
1565 background_box_border);
1567 // parse background_origin
1568 m_bg.m_origin = (background_box) value_index(
1569 get_style_property(_t("background-origin"), false, _t("padding-box")),
1570 background_box_strings,
1571 background_box_content);
1573 // parse background-image
1574 css::parse_css_url(get_style_property(_t("background-image"), false, _t("")), m_bg.m_image);
1575 m_bg.m_baseurl = get_style_property(_t("background-image-baseurl"), false, _t(""));
1577 if(!m_bg.m_image.empty())
1579 doc->container()->load_image(m_bg.m_image.c_str(), m_bg.m_baseurl.empty() ? 0 : m_bg.m_baseurl.c_str(), true);
1583 void litehtml::html_tag::add_positioned(const element::ptr &el)
1585 if (m_el_position != element_position_static || (!have_parent()))
1587 m_positioned.push_back(el);
1590 element::ptr el_parent = parent();
1593 el_parent->add_positioned(el);
1598 void litehtml::html_tag::calc_outlines( int parent_width )
1600 m_padding.left = m_css_padding.left.calc_percent(parent_width);
1601 m_padding.right = m_css_padding.right.calc_percent(parent_width);
1603 m_borders.left = m_css_borders.left.width.calc_percent(parent_width);
1604 m_borders.right = m_css_borders.right.width.calc_percent(parent_width);
1606 m_margins.left = m_css_margins.left.calc_percent(parent_width);
1607 m_margins.right = m_css_margins.right.calc_percent(parent_width);
1609 m_margins.top = m_css_margins.top.calc_percent(parent_width);
1610 m_margins.bottom = m_css_margins.bottom.calc_percent(parent_width);
1612 m_padding.top = m_css_padding.top.calc_percent(parent_width);
1613 m_padding.bottom = m_css_padding.bottom.calc_percent(parent_width);
1616 void litehtml::html_tag::calc_auto_margins(int parent_width)
1618 if (get_element_position() != element_position_absolute && (m_display == display_block || m_display == display_table))
1620 if (m_css_margins.left.is_predefined() && m_css_margins.right.is_predefined())
1622 int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right;
1623 if (el_width <= parent_width)
1625 m_margins.left = (parent_width - el_width) / 2;
1626 m_margins.right = (parent_width - el_width) - m_margins.left;
1631 m_margins.right = 0;
1634 else if (m_css_margins.left.is_predefined() && !m_css_margins.right.is_predefined())
1636 int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.right;
1637 m_margins.left = parent_width - el_width;
1638 if (m_margins.left < 0) m_margins.left = 0;
1640 else if (!m_css_margins.left.is_predefined() && m_css_margins.right.is_predefined())
1642 int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.left;
1643 m_margins.right = parent_width - el_width;
1644 if (m_margins.right < 0) m_margins.right = 0;
1649 void litehtml::html_tag::parse_attributes()
1651 for(auto& el : m_children)
1653 el->parse_attributes();
1657 void litehtml::html_tag::get_text( tstring& text )
1659 for (auto& el : m_children)
1665 bool litehtml::html_tag::is_body() const
1670 void litehtml::html_tag::set_data( const tchar_t* data )
1675 void litehtml::html_tag::get_inline_boxes( position::vector& boxes )
1677 litehtml::box* old_box = 0;
1679 for(auto& el : m_children)
1685 if(el->m_box != old_box)
1691 pos.x -= m_padding.left + m_borders.left;
1692 pos.width += m_padding.left + m_borders.left;
1694 boxes.push_back(pos);
1696 old_box = el->m_box;
1697 pos.x = el->left() + el->margin_left();
1698 pos.y = el->top() - m_padding.top - m_borders.top;
1702 pos.width = el->right() - pos.x - el->margin_right() - el->margin_left();
1703 pos.height = std::max(pos.height, el->height() + m_padding.top + m_padding.bottom + m_borders.top + m_borders.bottom);
1704 } else if(el->get_display() == display_inline)
1706 position::vector sub_boxes;
1707 el->get_inline_boxes(sub_boxes);
1708 if(!sub_boxes.empty())
1710 sub_boxes.rbegin()->width += el->margin_right();
1713 if(m_padding.left + m_borders.left > 0)
1715 position padding_box = (*sub_boxes.begin());
1716 padding_box.x -= m_padding.left + m_borders.left + el->margin_left();
1717 padding_box.width = m_padding.left + m_borders.left + el->margin_left();
1718 boxes.push_back(padding_box);
1722 sub_boxes.rbegin()->width += el->margin_right();
1724 boxes.insert(boxes.end(), sub_boxes.begin(), sub_boxes.end());
1729 if(pos.width || pos.height)
1733 pos.x -= m_padding.left + m_borders.left;
1734 pos.width += m_padding.left + m_borders.left;
1736 boxes.push_back(pos);
1740 if(m_padding.right + m_borders.right > 0)
1742 boxes.back().width += m_padding.right + m_borders.right;
1747 bool litehtml::html_tag::on_mouse_over()
1751 element::ptr el = shared_from_this();
1754 if(el->set_pseudo_class(_t("hover"), true))
1764 bool litehtml::html_tag::find_styles_changes( position::vector& redraw_boxes, int x, int y )
1766 if(m_display == display_inline_text)
1773 for (used_selector::vector::iterator iter = m_used_styles.begin(); iter != m_used_styles.end() && !apply; iter++)
1775 if((*iter)->m_selector->is_media_valid())
1777 int res = select(*((*iter)->m_selector), true);
1778 if( (res == select_no_match && (*iter)->m_used) || (res == select_match && !(*iter)->m_used) )
1787 if(m_display == display_inline || m_display == display_table_row)
1789 position::vector boxes;
1790 get_inline_boxes(boxes);
1791 for(position::vector::iterator pos = boxes.begin(); pos != boxes.end(); pos++)
1795 redraw_boxes.push_back(*pos);
1799 position pos = m_pos;
1800 if(m_el_position != element_position_fixed)
1807 redraw_boxes.push_back(pos);
1814 for (auto& el : m_children)
1818 if(m_el_position != element_position_fixed)
1820 if(el->find_styles_changes(redraw_boxes, x + m_pos.x, y + m_pos.y))
1826 if(el->find_styles_changes(redraw_boxes, m_pos.x, m_pos.y))
1836 bool litehtml::html_tag::on_mouse_leave()
1840 element::ptr el = shared_from_this();
1843 if(el->set_pseudo_class(_t("hover"), false))
1847 if(el->set_pseudo_class(_t("active"), false))
1857 bool litehtml::html_tag::on_lbutton_down()
1861 element::ptr el = shared_from_this();
1864 if (el->set_pseudo_class(_t("active"), true))
1874 bool litehtml::html_tag::on_lbutton_up()
1878 element::ptr el = shared_from_this();
1881 if (el->set_pseudo_class(_t("active"), false))
1893 void litehtml::html_tag::on_click()
1897 element::ptr el_parent = parent();
1900 el_parent->on_click();
1905 const litehtml::tchar_t* litehtml::html_tag::get_cursor()
1907 return get_style_property(_t("cursor"), true, 0);
1910 static const int font_size_table[8][7] =
1912 { 9, 9, 9, 9, 11, 14, 18},
1913 { 9, 9, 9, 10, 12, 15, 20},
1914 { 9, 9, 9, 11, 13, 17, 22},
1915 { 9, 9, 10, 12, 14, 18, 24},
1916 { 9, 9, 10, 13, 16, 20, 26},
1917 { 9, 9, 11, 14, 17, 21, 28},
1918 { 9, 10, 12, 15, 17, 23, 30},
1919 { 9, 10, 13, 16, 18, 24, 32}
1923 void litehtml::html_tag::init_font()
1925 // initialize font size
1926 const tchar_t* str = get_style_property(_t("font-size"), false, 0);
1929 int doc_font_size = get_document()->container()->get_default_font_size();
1930 element::ptr el_parent = parent();
1933 parent_sz = el_parent->get_font_size();
1936 parent_sz = doc_font_size;
1942 m_font_size = parent_sz;
1945 m_font_size = parent_sz;
1948 sz.fromString(str, font_size_strings);
1949 if(sz.is_predefined())
1951 int idx_in_table = doc_font_size - 9;
1952 if(idx_in_table >= 0 && idx_in_table <= 7)
1954 if(sz.predef() >= fontSize_xx_small && sz.predef() <= fontSize_xx_large)
1956 m_font_size = font_size_table[idx_in_table][sz.predef()];
1959 m_font_size = doc_font_size;
1965 case fontSize_xx_small:
1966 m_font_size = doc_font_size * 3 / 5;
1968 case fontSize_x_small:
1969 m_font_size = doc_font_size * 3 / 4;
1971 case fontSize_small:
1972 m_font_size = doc_font_size * 8 / 9;
1974 case fontSize_large:
1975 m_font_size = doc_font_size * 6 / 5;
1977 case fontSize_x_large:
1978 m_font_size = doc_font_size * 3 / 2;
1980 case fontSize_xx_large:
1981 m_font_size = doc_font_size * 2;
1984 m_font_size = doc_font_size;
1990 if(sz.units() == css_units_percentage)
1992 m_font_size = sz.calc_percent(parent_sz);
1993 } else if(sz.units() == css_units_none)
1995 m_font_size = parent_sz;
1998 m_font_size = get_document()->cvt_units(sz, parent_sz);
2004 const tchar_t* name = get_style_property(_t("font-family"), true, _t("inherit"));
2005 const tchar_t* weight = get_style_property(_t("font-weight"), true, _t("normal"));
2006 const tchar_t* style = get_style_property(_t("font-style"), true, _t("normal"));
2007 const tchar_t* decoration = get_style_property(_t("text-decoration"), true, _t("none"));
2009 m_font = get_document()->get_font(name, m_font_size, weight, style, decoration, &m_font_metrics);
2012 bool litehtml::html_tag::is_break() const
2017 void litehtml::html_tag::set_tagName( const tchar_t* tag )
2019 tstring s_val = tag;
2020 std::locale lc = std::locale::global(std::locale(""));
2021 for(size_t i = 0; i < s_val.length(); i++)
2023 s_val[i] = std::tolower(s_val[i], lc);
2028 void litehtml::html_tag::draw_background( uint_ptr hdc, int x, int y, const position* clip )
2030 position pos = m_pos;
2034 position el_pos = pos;
2035 el_pos += m_padding;
2036 el_pos += m_borders;
2038 if(m_display != display_inline && m_display != display_table_row)
2040 if(el_pos.does_intersect(clip))
2042 const background* bg = get_background();
2045 background_paint bg_paint;
2046 init_background_paint(pos, bg_paint, bg);
2048 get_document()->container()->draw_background(hdc, bg_paint);
2050 position border_box = pos;
2051 border_box += m_padding;
2052 border_box += m_borders;
2054 borders bdr = m_css_borders;
2055 bdr.radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);
2057 get_document()->container()->draw_borders(hdc, bdr, border_box, have_parent() ? false : true);
2061 const background* bg = get_background();
2063 position::vector boxes;
2064 get_inline_boxes(boxes);
2066 background_paint bg_paint;
2067 position content_box;
2069 for(position::vector::iterator box = boxes.begin(); box != boxes.end(); box++)
2074 if(box->does_intersect(clip))
2077 content_box -= m_borders;
2078 content_box -= m_padding;
2082 init_background_paint(content_box, bg_paint, bg);
2087 // set left borders radius for the first box
2088 if(box == boxes.begin())
2090 bdr.radius.bottom_left_x = m_css_borders.radius.bottom_left_x;
2091 bdr.radius.bottom_left_y = m_css_borders.radius.bottom_left_y;
2092 bdr.radius.top_left_x = m_css_borders.radius.top_left_x;
2093 bdr.radius.top_left_y = m_css_borders.radius.top_left_y;
2096 // set right borders radius for the last box
2097 if(box == boxes.end() - 1)
2099 bdr.radius.bottom_right_x = m_css_borders.radius.bottom_right_x;
2100 bdr.radius.bottom_right_y = m_css_borders.radius.bottom_right_y;
2101 bdr.radius.top_right_x = m_css_borders.radius.top_right_x;
2102 bdr.radius.top_right_y = m_css_borders.radius.top_right_y;
2106 bdr.top = m_css_borders.top;
2107 bdr.bottom = m_css_borders.bottom;
2108 if(box == boxes.begin())
2110 bdr.left = m_css_borders.left;
2112 if(box == boxes.end() - 1)
2114 bdr.right = m_css_borders.right;
2120 bg_paint.border_radius = bdr.radius.calc_percents(bg_paint.border_box.width, bg_paint.border_box.width);
2121 get_document()->container()->draw_background(hdc, bg_paint);
2124 b.radius = bdr.radius.calc_percents(box->width, box->height);
2125 get_document()->container()->draw_borders(hdc, b, *box, false);
2131 int litehtml::html_tag::render_inline(const element::ptr &container, int max_width)
2136 white_space ws = get_white_space();
2137 bool skip_spaces = false;
2138 if (ws == white_space_normal ||
2139 ws == white_space_nowrap ||
2140 ws == white_space_pre_line)
2144 bool was_space = false;
2146 for (auto& el : m_children)
2148 // skip spaces to make rendering a bit faster
2151 if (el->is_white_space())
2169 rw = container->place_element( el, max_width );
2178 int litehtml::html_tag::place_element(const element::ptr &el, int max_width)
2180 if(el->get_display() == display_none) return 0;
2182 if(el->get_display() == display_inline)
2184 return el->render_inline(shared_from_this(), max_width);
2187 element_position el_position = el->get_element_position();
2189 if(el_position == element_position_absolute || el_position == element_position_fixed)
2192 if(!m_boxes.empty())
2194 if(m_boxes.back()->get_type() == box_line)
2196 line_top = m_boxes.back()->top();
2197 if(!m_boxes.back()->is_empty())
2199 line_top += line_height();
2203 line_top = m_boxes.back()->bottom();
2207 el->render(0, line_top, max_width);
2208 el->m_pos.x += el->content_margins_left();
2209 el->m_pos.y += el->content_margins_top();
2216 switch(el->get_float())
2221 if(!m_boxes.empty())
2223 if(m_boxes.back()->get_type() == box_line)
2225 line_top = m_boxes.back()->top();
2228 line_top = m_boxes.back()->bottom();
2231 line_top = get_cleared_top(el, line_top);
2233 int line_right = max_width;
2234 get_line_left_right(line_top, max_width, line_left, line_right);
2236 el->render(line_left, line_top, line_right);
2237 if(el->right() > line_right)
2239 int new_top = find_next_line_top(el->top(), el->width(), max_width);
2240 el->m_pos.x = get_line_left(new_top) + el->content_margins_left();
2241 el->m_pos.y = new_top + el->content_margins_top();
2243 add_float(el, 0, 0);
2244 ret_width = fix_line_width(max_width, float_left);
2247 ret_width = el->right();
2254 if(!m_boxes.empty())
2256 if(m_boxes.back()->get_type() == box_line)
2258 line_top = m_boxes.back()->top();
2261 line_top = m_boxes.back()->bottom();
2264 line_top = get_cleared_top(el, line_top);
2266 int line_right = max_width;
2267 get_line_left_right(line_top, max_width, line_left, line_right);
2269 el->render(0, line_top, line_right);
2271 if(line_left + el->width() > line_right)
2273 int new_top = find_next_line_top(el->top(), el->width(), max_width);
2274 el->m_pos.x = get_line_right(new_top, max_width) - el->width() + el->content_margins_left();
2275 el->m_pos.y = new_top + el->content_margins_top();
2278 el->m_pos.x = line_right - el->width() + el->content_margins_left();
2280 add_float(el, 0, 0);
2281 ret_width = fix_line_width(max_width, float_right);
2286 line_right = max_width;
2287 get_line_left_right(line_top, max_width, line_left, line_right);
2289 ret_width = ret_width + (max_width - line_right);
2295 line_context line_ctx;
2297 if (!m_boxes.empty())
2299 line_ctx.top = m_boxes.back()->top();
2302 line_ctx.right = max_width;
2304 get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
2306 switch(el->get_display())
2308 case display_inline_block:
2309 ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.right);
2312 if(el->is_replaced() || el->is_floats_holder())
2314 element::ptr el_parent = el->parent();
2315 el->m_pos.width = el->get_css_width().calc_percent(line_ctx.right - line_ctx.left);
2316 el->m_pos.height = el->get_css_height().calc_percent(el_parent ? el_parent->m_pos.height : 0);
2318 el->calc_outlines(line_ctx.right - line_ctx.left);
2320 case display_inline_text:
2323 el->get_content_size(sz, line_ctx.right);
2332 bool add_box = true;
2333 if(!m_boxes.empty())
2335 if(m_boxes.back()->can_hold(el, m_white_space))
2342 new_box(el, max_width, line_ctx);
2343 } else if(!m_boxes.empty())
2345 line_ctx.top = m_boxes.back()->top();
2348 if (line_ctx.top != line_ctx.calculatedTop)
2351 line_ctx.right = max_width;
2353 get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
2356 if(!el->is_inline_box())
2358 if(m_boxes.size() == 1)
2360 if(collapse_top_margin())
2362 int shift = el->margin_top();
2365 line_ctx.top -= shift;
2366 m_boxes.back()->y_shift(-shift);
2372 int prev_margin = m_boxes[m_boxes.size() - 2]->bottom_margin();
2374 if(prev_margin > el->margin_top())
2376 shift = el->margin_top();
2379 shift = prev_margin;
2383 line_ctx.top -= shift;
2384 m_boxes.back()->y_shift(-shift);
2389 switch(el->get_display())
2392 case display_list_item:
2393 ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.width());
2396 case display_table_cell:
2397 case display_table_caption:
2398 case display_table_row:
2399 if(el->is_replaced() || el->is_floats_holder())
2401 ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.width()) + line_ctx.left + (max_width - line_ctx.right);
2404 ret_width = el->render(0, line_ctx.top, max_width);
2412 m_boxes.back()->add_element(el);
2414 if(el->is_inline_box() && !el->skip())
2416 ret_width = el->right() + (max_width - line_ctx.right);
2425 bool litehtml::html_tag::set_pseudo_class( const tchar_t* pclass, bool add )
2430 if(std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), pclass) == m_pseudo_classes.end())
2432 m_pseudo_classes.push_back(pclass);
2437 string_vector::iterator pi = std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), pclass);
2438 if(pi != m_pseudo_classes.end())
2440 m_pseudo_classes.erase(pi);
2447 bool litehtml::html_tag::set_class( const tchar_t* pclass, bool add )
2449 string_vector classes;
2450 bool changed = false;
2452 split_string( pclass, classes, _t(" ") );
2456 for( auto & _class : classes )
2458 if(std::find(m_class_values.begin(), m_class_values.end(), _class) == m_class_values.end())
2460 m_class_values.push_back( std::move( _class ) );
2466 for( const auto & _class : classes )
2468 auto end = std::remove(m_class_values.begin(), m_class_values.end(), _class);
2470 if(end != m_class_values.end())
2472 m_class_values.erase(end, m_class_values.end());
2480 tstring class_string;
2481 join_string(class_string, m_class_values, _t(" "));
2482 set_attr(_t("class"), class_string.c_str());
2493 int litehtml::html_tag::line_height() const
2495 return m_line_height;
2498 bool litehtml::html_tag::is_replaced() const
2503 int litehtml::html_tag::finish_last_box(bool end_of_render)
2507 if(!m_boxes.empty())
2509 m_boxes.back()->finish(end_of_render);
2511 if(m_boxes.back()->is_empty())
2513 line_top = m_boxes.back()->top();
2517 if(!m_boxes.empty())
2519 line_top = m_boxes.back()->bottom();
2525 int litehtml::html_tag::new_box(const element::ptr &el, int max_width, line_context& line_ctx)
2527 line_ctx.top = get_cleared_top(el, finish_last_box());
2530 line_ctx.right = max_width;
2532 get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
2534 if(el->is_inline_box() || el->is_floats_holder())
2536 if (el->width() > line_ctx.right - line_ctx.left)
2538 line_ctx.top = find_next_line_top(line_ctx.top, el->width(), max_width);
2540 line_ctx.right = max_width;
2542 get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
2546 int first_line_margin = 0;
2547 if(m_boxes.empty() && m_list_style_type != list_style_type_none && m_list_style_position == list_style_position_inside)
2549 int sz_font = get_font_size();
2550 first_line_margin = sz_font;
2553 if(el->is_inline_box())
2555 int text_indent = 0;
2556 if(m_css_text_indent.val() != 0)
2558 bool line_box_found = false;
2559 for(box::vector::iterator iter = m_boxes.begin(); iter != m_boxes.end(); iter++)
2561 if((*iter)->get_type() == box_line)
2563 line_box_found = true;
2569 text_indent = m_css_text_indent.calc_percent(max_width);
2575 m_boxes.emplace_back(std::unique_ptr<line_box>(new line_box(line_ctx.top, line_ctx.left + first_line_margin + text_indent, line_ctx.right, line_height(), fm, m_text_align)));
2578 m_boxes.emplace_back(std::unique_ptr<block_box>(new block_box(line_ctx.top, line_ctx.left, line_ctx.right)));
2581 return line_ctx.top;
2584 int litehtml::html_tag::get_cleared_top(const element::ptr &el, int line_top) const
2586 switch(el->get_clear())
2590 int fh = get_left_floats_height();
2591 if(fh && fh > line_top)
2599 int fh = get_right_floats_height();
2600 if(fh && fh > line_top)
2608 int fh = get_floats_height();
2609 if(fh && fh > line_top)
2616 if(el->get_float() != float_none)
2618 int fh = get_floats_height(el->get_float());
2619 if(fh && fh > line_top)
2629 litehtml::style_display litehtml::html_tag::get_display() const
2634 litehtml::element_float litehtml::html_tag::get_float() const
2639 bool litehtml::html_tag::is_floats_holder() const
2641 if( m_display == display_inline_block ||
2642 m_display == display_table_cell ||
2645 m_float != float_none ||
2646 m_el_position == element_position_absolute ||
2647 m_el_position == element_position_fixed ||
2648 m_overflow > overflow_visible)
2655 bool litehtml::html_tag::is_first_child_inline(const element::ptr& el) const
2657 if(!m_children.empty())
2659 for (const auto& this_el : m_children)
2661 if (!this_el->is_white_space())
2667 if (this_el->get_display() == display_inline)
2669 if (this_el->have_inline_child())
2683 bool litehtml::html_tag::is_last_child_inline(const element::ptr& el)
2685 if(!m_children.empty())
2687 for (auto this_el = m_children.rbegin(); this_el < m_children.rend(); ++this_el)
2689 if (!(*this_el)->is_white_space())
2691 if (el == (*this_el))
2695 if ((*this_el)->get_display() == display_inline)
2697 if ((*this_el)->have_inline_child())
2711 litehtml::white_space litehtml::html_tag::get_white_space() const
2713 return m_white_space;
2716 litehtml::vertical_align litehtml::html_tag::get_vertical_align() const
2718 return m_vertical_align;
2721 litehtml::css_length litehtml::html_tag::get_css_left() const
2723 return m_css_offsets.left;
2726 litehtml::css_length litehtml::html_tag::get_css_right() const
2728 return m_css_offsets.right;
2731 litehtml::css_length litehtml::html_tag::get_css_top() const
2733 return m_css_offsets.top;
2736 litehtml::css_length litehtml::html_tag::get_css_bottom() const
2738 return m_css_offsets.bottom;
2742 litehtml::css_offsets litehtml::html_tag::get_css_offsets() const
2744 return m_css_offsets;
2747 litehtml::element_clear litehtml::html_tag::get_clear() const
2752 litehtml::css_length litehtml::html_tag::get_css_width() const
2757 litehtml::css_length litehtml::html_tag::get_css_height() const
2759 return m_css_height;
2762 size_t litehtml::html_tag::get_children_count() const
2764 return m_children.size();
2767 litehtml::element::ptr litehtml::html_tag::get_child( int idx ) const
2769 return m_children[idx];
2772 void litehtml::html_tag::set_css_width( css_length& w )
2777 void litehtml::html_tag::apply_vertical_align()
2779 if(!m_boxes.empty())
2782 int content_height = m_boxes.back()->bottom();
2784 if(m_pos.height > content_height)
2786 switch(m_vertical_align)
2789 add = (m_pos.height - content_height) / 2;
2792 add = m_pos.height - content_height;
2802 for(size_t i = 0; i < m_boxes.size(); i++)
2804 m_boxes[i]->y_shift(add);
2810 litehtml::element_position litehtml::html_tag::get_element_position(css_offsets* offsets) const
2812 if(offsets && m_el_position != element_position_static)
2814 *offsets = m_css_offsets;
2816 return m_el_position;
2819 void litehtml::html_tag::init_background_paint(position pos, background_paint &bg_paint, const background* bg)
2824 position content_box = pos;
2825 position padding_box = pos;
2826 padding_box += m_padding;
2827 position border_box = padding_box;
2828 border_box += m_borders;
2832 case litehtml::background_box_padding:
2833 bg_paint.clip_box = padding_box;
2835 case litehtml::background_box_content:
2836 bg_paint.clip_box = content_box;
2839 bg_paint.clip_box = border_box;
2843 switch(bg->m_origin)
2845 case litehtml::background_box_border:
2846 bg_paint.origin_box = border_box;
2848 case litehtml::background_box_content:
2849 bg_paint.origin_box = content_box;
2852 bg_paint.origin_box = padding_box;
2856 if(!bg_paint.image.empty())
2858 get_document()->container()->get_image_size(bg_paint.image.c_str(), bg_paint.baseurl.c_str(), bg_paint.image_size);
2859 if(bg_paint.image_size.width && bg_paint.image_size.height)
2861 litehtml::size img_new_sz = bg_paint.image_size;
2862 double img_ar_width = (double) bg_paint.image_size.width / (double) bg_paint.image_size.height;
2863 double img_ar_height = (double) bg_paint.image_size.height / (double) bg_paint.image_size.width;
2866 if(bg->m_position.width.is_predefined())
2868 switch(bg->m_position.width.predef())
2870 case litehtml::background_size_contain:
2871 if( (int) ((double) bg_paint.origin_box.width * img_ar_height) <= bg_paint.origin_box.height )
2873 img_new_sz.width = bg_paint.origin_box.width;
2874 img_new_sz.height = (int) ((double) bg_paint.origin_box.width * img_ar_height);
2877 img_new_sz.height = bg_paint.origin_box.height;
2878 img_new_sz.width = (int) ((double) bg_paint.origin_box.height * img_ar_width);
2881 case litehtml::background_size_cover:
2882 if( (int) ((double) bg_paint.origin_box.width * img_ar_height) >= bg_paint.origin_box.height )
2884 img_new_sz.width = bg_paint.origin_box.width;
2885 img_new_sz.height = (int) ((double) bg_paint.origin_box.width * img_ar_height);
2888 img_new_sz.height = bg_paint.origin_box.height;
2889 img_new_sz.width = (int) ((double) bg_paint.origin_box.height * img_ar_width);
2893 case litehtml::background_size_auto:
2894 if(!bg->m_position.height.is_predefined())
2896 img_new_sz.height = bg->m_position.height.calc_percent(bg_paint.origin_box.height);
2897 img_new_sz.width = (int) ((double) img_new_sz.height * img_ar_width);
2903 img_new_sz.width = bg->m_position.width.calc_percent(bg_paint.origin_box.width);
2904 if(bg->m_position.height.is_predefined())
2906 img_new_sz.height = (int) ((double) img_new_sz.width * img_ar_height);
2909 img_new_sz.height = bg->m_position.height.calc_percent(bg_paint.origin_box.height);
2913 bg_paint.image_size = img_new_sz;
2914 bg_paint.position_x = bg_paint.origin_box.x + (int) bg->m_position.x.calc_percent(bg_paint.origin_box.width - bg_paint.image_size.width);
2915 bg_paint.position_y = bg_paint.origin_box.y + (int) bg->m_position.y.calc_percent(bg_paint.origin_box.height - bg_paint.image_size.height);
2919 bg_paint.border_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);;
2920 bg_paint.border_box = border_box;
2921 bg_paint.is_root = have_parent() ? false : true;
2924 litehtml::visibility litehtml::html_tag::get_visibility() const
2926 return m_visibility;
2929 void litehtml::html_tag::draw_list_marker( uint_ptr hdc, const position &pos )
2933 const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0);
2937 css::parse_css_url(list_image, lm.image);
2938 lm.baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0);
2939 get_document()->container()->get_image_size(lm.image.c_str(), lm.baseurl, img_size);
2946 int ln_height = line_height();
2947 int sz_font = get_font_size();
2949 lm.pos.width = sz_font - sz_font * 2 / 3;
2950 lm.pos.height = sz_font - sz_font * 2 / 3;
2951 lm.pos.y = pos.y + ln_height / 2 - lm.pos.height / 2;
2953 if(img_size.width && img_size.height)
2955 if(lm.pos.y + img_size.height > pos.y + pos.height)
2957 lm.pos.y = pos.y + pos.height - img_size.height;
2959 if(img_size.width > lm.pos.width)
2961 lm.pos.x -= img_size.width - lm.pos.width;
2964 lm.pos.width = img_size.width;
2965 lm.pos.height = img_size.height;
2967 if(m_list_style_position == list_style_position_outside)
2969 lm.pos.x -= sz_font;
2972 lm.color = get_color(_t("color"), true, web_color(0, 0, 0));
2973 lm.marker_type = m_list_style_type;
2974 get_document()->container()->draw_list_marker(hdc, lm);
2977 void litehtml::html_tag::draw_children( uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex )
2979 if (m_display == display_table || m_display == display_inline_table)
2981 draw_children_table(hdc, x, y, clip, flag, zindex);
2985 draw_children_box(hdc, x, y, clip, flag, zindex);
2989 bool litehtml::html_tag::fetch_positioned()
2993 m_positioned.clear();
2995 litehtml::element_position el_pos;
2997 for(auto& el : m_children)
2999 el_pos = el->get_element_position();
3000 if (el_pos != element_position_static)
3004 if (!ret && (el_pos == element_position_absolute || el_pos == element_position_fixed))
3008 if(el->fetch_positioned())
3016 int litehtml::html_tag::get_zindex() const
3021 void litehtml::html_tag::render_positioned(render_type rt)
3023 position wnd_position;
3024 get_document()->container()->get_client_rect(wnd_position);
3026 element_position el_position;
3028 for (auto& el : m_positioned)
3030 el_position = el->get_element_position();
3033 if(el->get_display() != display_none)
3035 if(el_position == element_position_absolute)
3037 if(rt != render_fixed_only)
3041 } else if(el_position == element_position_fixed)
3043 if(rt != render_no_fixed)
3052 int parent_height = 0;
3053 int parent_width = 0;
3056 if(el_position == element_position_fixed)
3058 parent_height = wnd_position.height;
3059 parent_width = wnd_position.width;
3060 client_x = wnd_position.left();
3061 client_y = wnd_position.top();
3064 element::ptr el_parent = el->parent();
3067 parent_height = el_parent->height();
3068 parent_width = el_parent->width();
3072 css_length css_left = el->get_css_left();
3073 css_length css_right = el->get_css_right();
3074 css_length css_top = el->get_css_top();
3075 css_length css_bottom = el->get_css_bottom();
3077 bool need_render = false;
3079 css_length el_w = el->get_css_width();
3080 css_length el_h = el->get_css_height();
3083 int new_height = -1;
3084 if(el_w.units() == css_units_percentage && parent_width)
3086 new_width = el_w.calc_percent(parent_width);
3087 if(el->m_pos.width != new_width)
3090 el->m_pos.width = new_width;
3094 if(el_h.units() == css_units_percentage && parent_height)
3096 new_height = el_h.calc_percent(parent_height);
3097 if(el->m_pos.height != new_height)
3100 el->m_pos.height = new_height;
3107 if(el_position == element_position_fixed)
3109 if(!css_left.is_predefined() || !css_right.is_predefined())
3111 if(!css_left.is_predefined() && css_right.is_predefined())
3113 el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left();
3114 } else if(css_left.is_predefined() && !css_right.is_predefined())
3116 el->m_pos.x = parent_width - css_right.calc_percent(parent_width) - el->m_pos.width - el->content_margins_right();
3119 el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left();
3120 el->m_pos.width = parent_width - css_left.calc_percent(parent_width) - css_right.calc_percent(parent_width) - (el->content_margins_left() + el->content_margins_right());
3125 if(!css_top.is_predefined() || !css_bottom.is_predefined())
3127 if(!css_top.is_predefined() && css_bottom.is_predefined())
3129 el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top();
3130 } else if(css_top.is_predefined() && !css_bottom.is_predefined())
3132 el->m_pos.y = parent_height - css_bottom.calc_percent(parent_height) - el->m_pos.height - el->content_margins_bottom();
3135 el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top();
3136 el->m_pos.height = parent_height - css_top.calc_percent(parent_height) - css_bottom.calc_percent(parent_height) - (el->content_margins_top() + el->content_margins_bottom());
3142 if(!css_left.is_predefined() || !css_right.is_predefined())
3144 if(!css_left.is_predefined() && css_right.is_predefined())
3146 el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left() - m_padding.left;
3147 } else if(css_left.is_predefined() && !css_right.is_predefined())
3149 el->m_pos.x = m_pos.width + m_padding.right - css_right.calc_percent(parent_width) - el->m_pos.width - el->content_margins_right();
3152 el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left() - m_padding.left;
3153 el->m_pos.width = m_pos.width + m_padding.left + m_padding.right - css_left.calc_percent(parent_width) - css_right.calc_percent(parent_width) - (el->content_margins_left() + el->content_margins_right());
3154 if (new_width != -1)
3156 el->m_pos.x += (el->m_pos.width - new_width) / 2;
3157 el->m_pos.width = new_width;
3164 if(!css_top.is_predefined() || !css_bottom.is_predefined())
3166 if(!css_top.is_predefined() && css_bottom.is_predefined())
3168 el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top() - m_padding.top;
3169 } else if(css_top.is_predefined() && !css_bottom.is_predefined())
3171 el->m_pos.y = m_pos.height + m_padding.bottom - css_bottom.calc_percent(parent_height) - el->m_pos.height - el->content_margins_bottom();
3174 el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top() - m_padding.top;
3175 el->m_pos.height = m_pos.height + m_padding.top + m_padding.bottom - css_top.calc_percent(parent_height) - css_bottom.calc_percent(parent_height) - (el->content_margins_top() + el->content_margins_bottom());
3176 if (new_height != -1)
3178 el->m_pos.y += (el->m_pos.height - new_height) / 2;
3179 el->m_pos.height = new_height;
3191 element::ptr cur_el = el->parent();
3192 element::ptr this_el = shared_from_this();
3193 while(cur_el && cur_el != this_el)
3195 offset_x += cur_el->m_pos.x;
3196 offset_y += cur_el->m_pos.y;
3197 cur_el = cur_el->parent();
3199 if(cvt_x) el->m_pos.x -= offset_x;
3200 if(cvt_y) el->m_pos.y -= offset_y;
3205 position pos = el->m_pos;
3206 el->render(el->left(), el->top(), el->width(), true);
3210 if(el_position == element_position_fixed)
3213 el->get_redraw_box(fixed_pos);
3214 get_document()->add_fixed_box(fixed_pos);
3218 el->render_positioned();
3221 if(!m_positioned.empty())
3223 std::stable_sort(m_positioned.begin(), m_positioned.end(), [](const litehtml::element::ptr& _Left, const litehtml::element::ptr& _Right)
3225 return (_Left->get_zindex() < _Right->get_zindex());
3230 void litehtml::html_tag::draw_stacking_context( uint_ptr hdc, int x, int y, const position* clip, bool with_positioned )
3232 if(!is_visible()) return;
3234 std::map<int, bool> zindexes;
3237 for(elements_vector::iterator i = m_positioned.begin(); i != m_positioned.end(); i++)
3239 zindexes[(*i)->get_zindex()];
3242 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++)
3246 draw_children(hdc, x, y, clip, draw_positioned, idx->first);
3250 draw_children(hdc, x, y, clip, draw_block, 0);
3251 draw_children(hdc, x, y, clip, draw_floats, 0);
3252 draw_children(hdc, x, y, clip, draw_inlines, 0);
3255 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++)
3259 draw_children(hdc, x, y, clip, draw_positioned, idx->first);
3263 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++)
3267 draw_children(hdc, x, y, clip, draw_positioned, idx->first);
3273 litehtml::overflow litehtml::html_tag::get_overflow() const
3278 bool litehtml::html_tag::is_nth_child(const element::ptr& el, int num, int off, bool of_type) const
3281 for(const auto& child : m_children)
3283 if(child->get_display() != display_inline_text)
3285 if( (!of_type) || (of_type && !t_strcmp(el->get_tagName(), child->get_tagName())) )
3291 if((idx - off) >= 0 && (idx - off) % num == 0)
3296 } else if(idx == off)
3304 if(el == child) break;
3310 bool litehtml::html_tag::is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const
3313 for(elements_vector::const_reverse_iterator child = m_children.rbegin(); child != m_children.rend(); child++)
3315 if((*child)->get_display() != display_inline_text)
3317 if( !of_type || (of_type && !t_strcmp(el->get_tagName(), (*child)->get_tagName())) )
3323 if((idx - off) >= 0 && (idx - off) % num == 0)
3328 } else if(idx == off)
3336 if(el == (*child)) break;
3342 void litehtml::html_tag::parse_nth_child_params( tstring param, int &num, int &off )
3344 if(param == _t("odd"))
3348 } else if(param == _t("even"))
3354 string_vector tokens;
3355 split_string(param, tokens, _t(" n"), _t("n"));
3361 for(string_vector::iterator tok = tokens.begin(); tok != tokens.end(); tok++)
3363 if((*tok) == _t("n"))
3374 num = t_atoi(s_num.c_str());
3375 off = t_atoi(s_off.c_str());
3379 void litehtml::html_tag::calc_document_size( litehtml::size& sz, int x /*= 0*/, int y /*= 0*/ )
3381 if(is_visible() && m_el_position != element_position_fixed)
3383 element::calc_document_size(sz, x, y);
3385 if(m_overflow == overflow_visible)
3387 for(auto& el : m_children)
3389 el->calc_document_size(sz, x + m_pos.x, y + m_pos.y);
3393 // root element (<html>) must to cover entire window
3396 position client_pos;
3397 get_document()->container()->get_client_rect(client_pos);
3398 m_pos.height = std::max(sz.height, client_pos.height) - content_margins_top() - content_margins_bottom();
3399 m_pos.width = std::max(sz.width, client_pos.width) - content_margins_left() - content_margins_right();
3405 void litehtml::html_tag::get_redraw_box(litehtml::position& pos, int x /*= 0*/, int y /*= 0*/)
3409 element::get_redraw_box(pos, x, y);
3411 if(m_overflow == overflow_visible)
3413 for(auto& el : m_children)
3415 if(el->get_element_position() != element_position_fixed)
3417 el->get_redraw_box(pos, x + m_pos.x, y + m_pos.y);
3424 litehtml::element::ptr litehtml::html_tag::find_adjacent_sibling( const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/ )
3427 for(auto& e : m_children)
3429 if(e->get_display() != display_inline_text)
3435 int res = ret->select(selector, apply_pseudo);
3436 if(res != select_no_match)
3440 if(res & select_match_pseudo_class)
3461 litehtml::element::ptr litehtml::html_tag::find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/)
3463 element::ptr ret = 0;
3464 for(auto& e : m_children)
3466 if(e->get_display() != display_inline_text)
3473 int res = e->select(selector, apply_pseudo);
3474 if(res != select_no_match)
3478 if(res & select_match_pseudo_class)
3494 bool litehtml::html_tag::is_only_child(const element::ptr& el, bool of_type) const
3496 int child_count = 0;
3497 for(const auto& child : m_children)
3499 if(child->get_display() != display_inline_text)
3501 if( !of_type || (of_type && !t_strcmp(el->get_tagName(), child->get_tagName())) )
3505 if(child_count > 1) break;
3515 void litehtml::html_tag::update_floats(int dy, const element::ptr &parent)
3517 if(is_floats_holder())
3519 bool reset_cache = false;
3520 for(floated_box::vector::reverse_iterator fb = m_floats_left.rbegin(); fb != m_floats_left.rend(); fb++)
3522 if(fb->el->is_ancestor(parent))
3530 m_cahe_line_left.invalidate();
3532 reset_cache = false;
3533 for(floated_box::vector::reverse_iterator fb = m_floats_right.rbegin(); fb != m_floats_right.rend(); fb++)
3535 if(fb->el->is_ancestor(parent))
3543 m_cahe_line_right.invalidate();
3547 element::ptr el_parent = this->parent();
3550 el_parent->update_floats(dy, parent);
3555 void litehtml::html_tag::remove_before_after()
3557 if(!m_children.empty())
3559 if( !t_strcmp(m_children.front()->get_tagName(), _t("::before")) )
3561 m_children.erase(m_children.begin());
3564 if(!m_children.empty())
3566 if( !t_strcmp(m_children.back()->get_tagName(), _t("::after")) )
3568 m_children.erase(m_children.end() - 1);
3573 litehtml::element::ptr litehtml::html_tag::get_element_before()
3575 if(!m_children.empty())
3577 if( !t_strcmp(m_children.front()->get_tagName(), _t("::before")) )
3579 return m_children.front();
3582 element::ptr el = std::make_shared<el_before>(get_document());
3583 el->parent(shared_from_this());
3584 m_children.insert(m_children.begin(), el);
3588 litehtml::element::ptr litehtml::html_tag::get_element_after()
3590 if(!m_children.empty())
3592 if( !t_strcmp(m_children.back()->get_tagName(), _t("::after")) )
3594 return m_children.back();
3597 element::ptr el = std::make_shared<el_after>(get_document());
3602 void litehtml::html_tag::add_style( const litehtml::style& st )
3604 m_style.combine(st);
3607 bool litehtml::html_tag::have_inline_child() const
3609 if(!m_children.empty())
3611 for(const auto& el : m_children)
3613 if(!el->is_white_space())
3622 void litehtml::html_tag::refresh_styles()
3624 remove_before_after();
3626 for (auto& el : m_children)
3628 if(el->get_display() != display_inline_text)
3630 el->refresh_styles();
3636 for (auto& usel : m_used_styles)
3638 usel->m_used = false;
3640 if(usel->m_selector->is_media_valid())
3642 int apply = select(*usel->m_selector, false);
3644 if(apply != select_no_match)
3646 if(apply & select_match_pseudo_class)
3648 if(select(*usel->m_selector, true))
3650 if(apply & select_match_with_after)
3652 element::ptr el = get_element_after();
3655 el->add_style(*usel->m_selector->m_style);
3657 } else if(apply & select_match_with_before)
3659 element::ptr el = get_element_before();
3662 el->add_style(*usel->m_selector->m_style);
3667 add_style(*usel->m_selector->m_style);
3668 usel->m_used = true;
3671 } else if(apply & select_match_with_after)
3673 element::ptr el = get_element_after();
3676 el->add_style(*usel->m_selector->m_style);
3678 } else if(apply & select_match_with_before)
3680 element::ptr el = get_element_before();
3683 el->add_style(*usel->m_selector->m_style);
3687 add_style(*usel->m_selector->m_style);
3688 usel->m_used = true;
3695 litehtml::element::ptr litehtml::html_tag::get_child_by_point(int x, int y, int client_x, int client_y, draw_flag flag, int zindex)
3697 element::ptr ret = 0;
3699 if(m_overflow > overflow_visible)
3701 if(!m_pos.is_point_inside(x, y))
3707 position pos = m_pos;
3711 for(elements_vector::reverse_iterator i = m_children.rbegin(); i != m_children.rend() && !ret; i++)
3713 element::ptr el = (*i);
3715 if(el->is_visible() && el->get_display() != display_inline_text)
3719 case draw_positioned:
3720 if(el->is_positioned() && el->get_zindex() == zindex)
3722 if(el->get_element_position() == element_position_fixed)
3724 ret = el->get_element_by_point(client_x, client_y, client_x, client_y);
3725 if(!ret && (*i)->is_point_inside(client_x, client_y))
3731 ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y);
3732 if(!ret && (*i)->is_point_inside(pos.x, pos.y))
3741 if(!el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
3743 if(el->is_point_inside(pos.x, pos.y))
3750 if(el->get_float() != float_none && !el->is_positioned())
3752 ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y);
3754 if(!ret && (*i)->is_point_inside(pos.x, pos.y))
3762 if(el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
3764 if(el->get_display() == display_inline_block)
3766 ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y);
3769 if(!ret && (*i)->is_point_inside(pos.x, pos.y))
3779 if(el && !el->is_positioned())
3781 if(flag == draw_positioned)
3783 element::ptr child = el->get_child_by_point(pos.x, pos.y, client_x, client_y, flag, zindex);
3790 if( el->get_float() == float_none &&
3791 el->get_display() != display_inline_block)
3793 element::ptr child = el->get_child_by_point(pos.x, pos.y, client_x, client_y, flag, zindex);
3807 litehtml::element::ptr litehtml::html_tag::get_element_by_point(int x, int y, int client_x, int client_y)
3809 if(!is_visible()) return 0;
3813 std::map<int, bool> zindexes;
3815 for(elements_vector::iterator i = m_positioned.begin(); i != m_positioned.end(); i++)
3817 zindexes[(*i)->get_zindex()];
3820 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++)
3824 ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first);
3829 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++)
3833 ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first);
3838 ret = get_child_by_point(x, y, client_x, client_y, draw_inlines, 0);
3841 ret = get_child_by_point(x, y, client_x, client_y, draw_floats, 0);
3844 ret = get_child_by_point(x, y, client_x, client_y, draw_block, 0);
3848 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++)
3852 ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first);
3857 if(m_el_position == element_position_fixed)
3859 if(is_point_inside(client_x, client_y))
3861 ret = shared_from_this();
3865 if(is_point_inside(x, y))
3867 ret = shared_from_this();
3874 const litehtml::background* litehtml::html_tag::get_background(bool own_only)
3878 // return own background with check for empty one
3879 if(m_bg.m_image.empty() && !m_bg.m_color.alpha)
3886 if(m_bg.m_image.empty() && !m_bg.m_color.alpha)
3888 // if this is root element (<html>) try to get background from body
3891 for (const auto& el : m_children)
3895 // return own body background
3896 return el->get_background(true);
3905 element::ptr el_parent = parent();
3908 if (!el_parent->get_background(true))
3910 // parent of body will draw background for body
3919 int litehtml::html_tag::render_box(int x, int y, int max_width, bool second_pass /*= false*/)
3921 int parent_width = max_width;
3923 calc_outlines(parent_width);
3926 m_pos.move_to(x, y);
3928 m_pos.x += content_margins_left();
3929 m_pos.y += content_margins_top();
3933 def_value<int> block_width(0);
3935 if (m_display != display_table_cell && !m_css_width.is_predefined())
3937 int w = calc_width(parent_width);
3939 if (m_box_sizing == box_sizing_border_box)
3941 w -= m_padding.width() + m_borders.width();
3943 ret_width = max_width = block_width = w;
3949 max_width -= content_margins_left() + content_margins_right();
3953 // check for max-width (on the first pass only)
3954 if (!m_css_max_width.is_predefined() && !second_pass)
3956 int mw = get_document()->cvt_units(m_css_max_width, m_font_size, parent_width);
3957 if (m_box_sizing == box_sizing_border_box)
3959 mw -= m_padding.left + m_borders.left + m_padding.right + m_borders.right;
3967 m_floats_left.clear();
3968 m_floats_right.clear();
3970 m_cahe_line_left.invalidate();
3971 m_cahe_line_right.invalidate();
3973 element_position el_position;
3975 int block_height = 0;
3979 if (get_predefined_height(block_height))
3981 m_pos.height = block_height;
3984 white_space ws = get_white_space();
3985 bool skip_spaces = false;
3986 if (ws == white_space_normal ||
3987 ws == white_space_nowrap ||
3988 ws == white_space_pre_line)
3993 bool was_space = false;
3995 for (auto el : m_children)
3997 // we don't need process absolute and fixed positioned element on the second pass
4000 el_position = el->get_element_position();
4001 if ((el_position == element_position_absolute || el_position == element_position_fixed)) continue;
4004 // skip spaces to make rendering a bit faster
4007 if (el->is_white_space())
4025 // place element into rendering flow
4026 int rw = place_element(el, max_width);
4033 finish_last_box(true);
4035 if (block_width.is_default() && is_inline_box())
4037 m_pos.width = ret_width;
4041 m_pos.width = max_width;
4043 calc_auto_margins(parent_width);
4045 if (!m_boxes.empty())
4047 if (collapse_top_margin())
4049 int old_top = m_margins.top;
4050 m_margins.top = std::max(m_boxes.front()->top_margin(), m_margins.top);
4051 if (m_margins.top != old_top)
4053 update_floats(m_margins.top - old_top, shared_from_this());
4056 if (collapse_bottom_margin())
4058 m_margins.bottom = std::max(m_boxes.back()->bottom_margin(), m_margins.bottom);
4059 m_pos.height = m_boxes.back()->bottom() - m_boxes.back()->bottom_margin();
4063 m_pos.height = m_boxes.back()->bottom();
4067 // add the floats height to the block height
4068 if (is_floats_holder())
4070 int floats_height = get_floats_height();
4071 if (floats_height > m_pos.height)
4073 m_pos.height = floats_height;
4077 // calculate the final position
4079 m_pos.move_to(x, y);
4080 m_pos.x += content_margins_left();
4081 m_pos.y += content_margins_top();
4083 if (get_predefined_height(block_height))
4085 m_pos.height = block_height;
4089 if (!m_css_min_height.is_predefined() && m_css_min_height.units() == css_units_percentage)
4091 element::ptr el_parent = parent();
4094 if (el_parent->get_predefined_height(block_height))
4096 min_height = m_css_min_height.calc_percent(block_height);
4102 min_height = (int)m_css_min_height.val();
4104 if (min_height != 0 && m_box_sizing == box_sizing_border_box)
4106 min_height -= m_padding.top + m_borders.top + m_padding.bottom + m_borders.bottom;
4107 if (min_height < 0) min_height = 0;
4110 if (m_display == display_list_item)
4112 const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0);
4116 css::parse_css_url(list_image, url);
4119 const tchar_t* list_image_baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0);
4120 get_document()->container()->get_image_size(url.c_str(), list_image_baseurl, sz);
4121 if (min_height < sz.height)
4123 min_height = sz.height;
4129 if (min_height > m_pos.height)
4131 m_pos.height = min_height;
4134 int min_width = m_css_min_width.calc_percent(parent_width);
4136 if (min_width != 0 && m_box_sizing == box_sizing_border_box)
4138 min_width -= m_padding.left + m_borders.left + m_padding.right + m_borders.right;
4139 if (min_width < 0) min_width = 0;
4144 if (min_width > m_pos.width)
4146 m_pos.width = min_width;
4148 if (min_width > ret_width)
4150 ret_width = min_width;
4154 ret_width += content_margins_left() + content_margins_right();
4156 // re-render with new width
4157 if (ret_width < max_width && !second_pass && have_parent())
4159 if (m_display == display_inline_block ||
4160 m_css_width.is_predefined() &&
4161 (m_float != float_none ||
4162 m_display == display_table ||
4163 m_el_position == element_position_absolute ||
4164 m_el_position == element_position_fixed
4168 render(x, y, ret_width, true);
4169 m_pos.width = ret_width - (content_margins_left() + content_margins_right());
4173 if (is_floats_holder() && !second_pass)
4175 for (const auto& fb : m_floats_left)
4177 fb.el->apply_relative_shift(fb.el->parent()->calc_width(m_pos.width));
4185 int litehtml::html_tag::render_table(int x, int y, int max_width, bool second_pass /*= false*/)
4187 if (!m_grid) return 0;
4189 int parent_width = max_width;
4191 calc_outlines(parent_width);
4194 m_pos.move_to(x, y);
4196 m_pos.x += content_margins_left();
4197 m_pos.y += content_margins_top();
4199 def_value<int> block_width(0);
4201 if (!m_css_width.is_predefined())
4203 max_width = block_width = calc_width(parent_width) - m_padding.width() - m_borders.width();
4209 max_width -= content_margins_left() + content_margins_right();
4213 // Calculate table spacing
4214 int table_width_spacing = 0;
4215 if (m_border_collapse == border_collapse_separate)
4217 table_width_spacing = m_border_spacing_x * (m_grid->cols_count() + 1);
4221 table_width_spacing = 0;
4223 if (m_grid->cols_count())
4225 table_width_spacing -= std::min(border_left(), m_grid->column(0).border_left);
4226 table_width_spacing -= std::min(border_right(), m_grid->column(m_grid->cols_count() - 1).border_right);
4229 for (int col = 1; col < m_grid->cols_count(); col++)
4231 table_width_spacing -= std::min(m_grid->column(col).border_left, m_grid->column(col - 1).border_right);
4236 // 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.
4237 // 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
4240 // Also, calculate the "maximum" cell width of each cell: formatting the content without breaking lines other than where explicit line breaks occur.
4242 if (m_grid->cols_count() == 1 && !block_width.is_default())
4244 for (int row = 0; row < m_grid->rows_count(); row++)
4246 table_cell* cell = m_grid->cell(0, row);
4247 if (cell && cell->el)
4249 cell->min_width = cell->max_width = cell->el->render(0, 0, max_width - table_width_spacing);
4250 cell->el->m_pos.width = cell->min_width - cell->el->content_margins_left() - cell->el->content_margins_right();
4256 for (int row = 0; row < m_grid->rows_count(); row++)
4258 for (int col = 0; col < m_grid->cols_count(); col++)
4260 table_cell* cell = m_grid->cell(col, row);
4261 if (cell && cell->el)
4263 if (!m_grid->column(col).css_width.is_predefined() && m_grid->column(col).css_width.units() != css_units_percentage)
4265 int css_w = m_grid->column(col).css_width.calc_percent(block_width);
4266 int el_w = cell->el->render(0, 0, css_w);
4267 cell->min_width = cell->max_width = std::max(css_w, el_w);
4268 cell->el->m_pos.width = cell->min_width - cell->el->content_margins_left() - cell->el->content_margins_right();
4272 // calculate minimum content width
4273 cell->min_width = cell->el->render(0, 0, 1);
4274 // calculate maximum content width
4275 cell->max_width = cell->el->render(0, 0, max_width - table_width_spacing);
4282 // For each column, determine a maximum and minimum column width from the cells that span only that column.
4283 // The minimum is that required by the cell with the largest minimum cell width (or the column 'width', whichever is larger).
4284 // The maximum is that required by the cell with the largest maximum cell width (or the column 'width', whichever is larger).
4286 for (int col = 0; col < m_grid->cols_count(); col++)
4288 m_grid->column(col).max_width = 0;
4289 m_grid->column(col).min_width = 0;
4290 for (int row = 0; row < m_grid->rows_count(); row++)
4292 if (m_grid->cell(col, row)->colspan <= 1)
4294 m_grid->column(col).max_width = std::max(m_grid->column(col).max_width, m_grid->cell(col, row)->max_width);
4295 m_grid->column(col).min_width = std::max(m_grid->column(col).min_width, m_grid->cell(col, row)->min_width);
4300 // For each cell that spans more than one column, increase the minimum widths of the columns it spans so that together,
4301 // they are at least as wide as the cell. Do the same for the maximum widths.
4302 // If possible, widen all spanned columns by approximately the same amount.
4304 for (int col = 0; col < m_grid->cols_count(); col++)
4306 for (int row = 0; row < m_grid->rows_count(); row++)
4308 if (m_grid->cell(col, row)->colspan > 1)
4310 int max_total_width = m_grid->column(col).max_width;
4311 int min_total_width = m_grid->column(col).min_width;
4312 for (int col2 = col + 1; col2 < col + m_grid->cell(col, row)->colspan; col2++)
4314 max_total_width += m_grid->column(col2).max_width;
4315 min_total_width += m_grid->column(col2).min_width;
4317 if (min_total_width < m_grid->cell(col, row)->min_width)
4319 m_grid->distribute_min_width(m_grid->cell(col, row)->min_width - min_total_width, col, col + m_grid->cell(col, row)->colspan - 1);
4321 if (max_total_width < m_grid->cell(col, row)->max_width)
4323 m_grid->distribute_max_width(m_grid->cell(col, row)->max_width - max_total_width, col, col + m_grid->cell(col, row)->colspan - 1);
4329 // If the 'table' or 'inline-table' element's 'width' property has a computed value (W) other than 'auto', the used width is the
4330 // greater of W, CAPMIN, and the minimum width required by all the columns plus cell spacing or borders (MIN).
4331 // If the used width is greater than MIN, the extra width should be distributed over the columns.
4333 // If the 'table' or 'inline-table' element has 'width: auto', the used width is the greater of the table's containing block width,
4334 // CAPMIN, and MIN. However, if either CAPMIN or the maximum width required by the columns plus cell spacing or borders (MAX) is
4335 // less than that of the containing block, use max(MAX, CAPMIN).
4338 int table_width = 0;
4339 int min_table_width = 0;
4340 int max_table_width = 0;
4342 if (!block_width.is_default())
4344 table_width = m_grid->calc_table_width(block_width - table_width_spacing, false, min_table_width, max_table_width);
4348 table_width = m_grid->calc_table_width(max_width - table_width_spacing, true, min_table_width, max_table_width);
4351 min_table_width += table_width_spacing;
4352 max_table_width += table_width_spacing;
4353 table_width += table_width_spacing;
4354 m_grid->calc_horizontal_positions(m_borders, m_border_collapse, m_border_spacing_x);
4356 bool row_span_found = false;
4358 // render cells with computed width
4359 for (int row = 0; row < m_grid->rows_count(); row++)
4361 m_grid->row(row).height = 0;
4362 for (int col = 0; col < m_grid->cols_count(); col++)
4364 table_cell* cell = m_grid->cell(col, row);
4367 int span_col = col + cell->colspan - 1;
4368 if (span_col >= m_grid->cols_count())
4370 span_col = m_grid->cols_count() - 1;
4372 int cell_width = m_grid->column(span_col).right - m_grid->column(col).left;
4374 if (cell->el->m_pos.width != cell_width - cell->el->content_margins_left() - cell->el->content_margins_right())
4376 cell->el->render(m_grid->column(col).left, 0, cell_width);
4377 cell->el->m_pos.width = cell_width - cell->el->content_margins_left() - cell->el->content_margins_right();
4381 cell->el->m_pos.x = m_grid->column(col).left + cell->el->content_margins_left();
4384 if (cell->rowspan <= 1)
4386 m_grid->row(row).height = std::max(m_grid->row(row).height, cell->el->height());
4390 row_span_found = true;
4399 for (int col = 0; col < m_grid->cols_count(); col++)
4401 for (int row = 0; row < m_grid->rows_count(); row++)
4403 table_cell* cell = m_grid->cell(col, row);
4406 int span_row = row + cell->rowspan - 1;
4407 if (span_row >= m_grid->rows_count())
4409 span_row = m_grid->rows_count() - 1;
4411 if (span_row != row)
4414 for (int i = row; i <= span_row; i++)
4416 h += m_grid->row(i).height;
4418 if (h < cell->el->height())
4420 m_grid->row(span_row).height += cell->el->height() - h;
4428 // Calculate vertical table spacing
4429 int table_height_spacing = 0;
4430 if (m_border_collapse == border_collapse_separate)
4432 table_height_spacing = m_border_spacing_y * (m_grid->rows_count() + 1);
4436 table_height_spacing = 0;
4438 if (m_grid->rows_count())
4440 table_height_spacing -= std::min(border_top(), m_grid->row(0).border_top);
4441 table_height_spacing -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom);
4444 for (int row = 1; row < m_grid->rows_count(); row++)
4446 table_height_spacing -= std::min(m_grid->row(row).border_top, m_grid->row(row - 1).border_bottom);
4451 // calculate block height
4452 int block_height = 0;
4453 if (get_predefined_height(block_height))
4455 block_height -= m_padding.height() + m_borders.height();
4458 // calculate minimum height from m_css_min_height
4460 if (!m_css_min_height.is_predefined() && m_css_min_height.units() == css_units_percentage)
4462 element::ptr el_parent = parent();
4465 int parent_height = 0;
4466 if (el_parent->get_predefined_height(parent_height))
4468 min_height = m_css_min_height.calc_percent(parent_height);
4474 min_height = (int)m_css_min_height.val();
4477 int extra_row_height = 0;
4478 int minimum_table_height = std::max(block_height, min_height);
4480 m_grid->calc_rows_height(minimum_table_height - table_height_spacing, m_border_spacing_y);
4481 m_grid->calc_vertical_positions(m_borders, m_border_collapse, m_border_spacing_y);
4483 int table_height = 0;
4485 // place cells vertically
4486 for (int col = 0; col < m_grid->cols_count(); col++)
4488 for (int row = 0; row < m_grid->rows_count(); row++)
4490 table_cell* cell = m_grid->cell(col, row);
4493 int span_row = row + cell->rowspan - 1;
4494 if (span_row >= m_grid->rows_count())
4496 span_row = m_grid->rows_count() - 1;
4498 cell->el->m_pos.y = m_grid->row(row).top + cell->el->content_margins_top();
4499 cell->el->m_pos.height = m_grid->row(span_row).bottom - m_grid->row(row).top - cell->el->content_margins_top() - cell->el->content_margins_bottom();
4500 table_height = std::max(table_height, m_grid->row(span_row).bottom);
4501 cell->el->apply_vertical_align();
4506 if (m_border_collapse == border_collapse_collapse)
4508 if (m_grid->rows_count())
4510 table_height -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom);
4515 table_height += m_border_spacing_y;
4518 m_pos.width = table_width;
4520 calc_auto_margins(parent_width);
4522 m_pos.move_to(x, y);
4523 m_pos.x += content_margins_left();
4524 m_pos.y += content_margins_top();
4525 m_pos.width = table_width;
4526 m_pos.height = table_height;
4528 return max_table_width;
4531 void litehtml::html_tag::draw_children_box(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex)
4533 position pos = m_pos;
4537 document::ptr doc = get_document();
4539 if (m_overflow > overflow_visible)
4541 position border_box = pos;
4542 border_box += m_padding;
4543 border_box += m_borders;
4545 border_radiuses bdr_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);
4547 bdr_radius -= m_borders;
4548 bdr_radius -= m_padding;
4550 doc->container()->set_clip(pos, bdr_radius, true, true);
4553 position browser_wnd;
4554 doc->container()->get_client_rect(browser_wnd);
4557 for (auto& item : m_children)
4560 if (el->is_visible())
4564 case draw_positioned:
4565 if (el->is_positioned() && el->get_zindex() == zindex)
4567 if (el->get_element_position() == element_position_fixed)
4569 el->draw(hdc, browser_wnd.x, browser_wnd.y, clip);
4570 el->draw_stacking_context(hdc, browser_wnd.x, browser_wnd.y, clip, true);
4574 el->draw(hdc, pos.x, pos.y, clip);
4575 el->draw_stacking_context(hdc, pos.x, pos.y, clip, true);
4581 if (!el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
4583 el->draw(hdc, pos.x, pos.y, clip);
4587 if (el->get_float() != float_none && !el->is_positioned())
4589 el->draw(hdc, pos.x, pos.y, clip);
4590 el->draw_stacking_context(hdc, pos.x, pos.y, clip, false);
4595 if (el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
4597 el->draw(hdc, pos.x, pos.y, clip);
4598 if (el->get_display() == display_inline_block)
4600 el->draw_stacking_context(hdc, pos.x, pos.y, clip, false);
4611 if (flag == draw_positioned)
4613 if (!el->is_positioned())
4615 el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
4620 if (el->get_float() == float_none &&
4621 el->get_display() != display_inline_block &&
4622 !el->is_positioned())
4624 el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
4631 if (m_overflow > overflow_visible)
4633 doc->container()->del_clip();
4637 void litehtml::html_tag::draw_children_table(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex)
4639 if (!m_grid) return;
4641 position pos = m_pos;
4644 for (int row = 0; row < m_grid->rows_count(); row++)
4646 if (flag == draw_block)
4648 m_grid->row(row).el_row->draw_background(hdc, pos.x, pos.y, clip);
4650 for (int col = 0; col < m_grid->cols_count(); col++)
4652 table_cell* cell = m_grid->cell(col, row);
4655 if (flag == draw_block)
4657 cell->el->draw(hdc, pos.x, pos.y, clip);
4659 cell->el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);