#include "html.h" #include "css_properties.h" #include #define offset(member) ((uint_ptr)&this->member - (uint_ptr)this) void litehtml::css_properties::compute(const html_tag* el, const document::ptr& doc) { compute_font(el, doc); int font_size = get_font_size(); m_color = el->get_property(_color_, true, web_color::black, offset(m_color)); m_el_position = (element_position) el->get_property( _position_, false, element_position_static, offset(m_el_position)); m_display = (style_display) el->get_property( _display_, false, display_inline, offset(m_display)); m_visibility = (visibility) el->get_property( _visibility_, true, visibility_visible, offset(m_visibility)); m_float = (element_float) el->get_property( _float_, false, float_none, offset(m_float)); m_clear = (element_clear) el->get_property( _clear_, false, clear_none, offset(m_clear)); m_box_sizing = (box_sizing) el->get_property( _box_sizing_, false, box_sizing_content_box, offset(m_box_sizing)); m_overflow = (overflow) el->get_property( _overflow_, false, overflow_visible, offset(m_overflow)); m_text_align = (text_align) el->get_property( _text_align_, true, text_align_left, offset(m_text_align)); m_vertical_align = (vertical_align) el->get_property( _vertical_align_, false, va_baseline, offset(m_vertical_align)); m_text_transform = (text_transform) el->get_property( _text_transform_, true, text_transform_none, offset(m_text_transform)); m_white_space = (white_space) el->get_property( _white_space_, true, white_space_normal, offset(m_white_space)); m_caption_side = (caption_side) el->get_property( _caption_side_, true, caption_side_top, offset(m_caption_side)); // https://www.w3.org/TR/CSS22/visuren.html#dis-pos-flo if (m_display == display_none) { // 1. If 'display' has the value 'none', then 'position' and 'float' do not apply. In this case, the element // generates no box. m_float = float_none; } else { // 2. Otherwise, if 'position' has the value 'absolute' or 'fixed', the box is absolutely positioned, // the computed value of 'float' is 'none', and display is set according to the table below. // The position of the box will be determined by the 'top', 'right', 'bottom' and 'left' properties // and the box's containing block. if (m_el_position == element_position_absolute || m_el_position == element_position_fixed) { m_float = float_none; if (m_display == display_inline_table) { m_display = display_table; } else if (m_display == display_inline || m_display == display_table_row_group || m_display == display_table_column || m_display == display_table_column_group || m_display == display_table_header_group || m_display == display_table_footer_group || m_display == display_table_row || m_display == display_table_cell || m_display == display_table_caption || m_display == display_inline_block) { m_display = display_block; } } else if (m_float != float_none) { // 3. Otherwise, if 'float' has a value other than 'none', the box is floated and 'display' is set // according to the table below. if (m_display == display_inline_table) { m_display = display_table; } else if (m_display == display_inline || m_display == display_table_row_group || m_display == display_table_column || m_display == display_table_column_group || m_display == display_table_header_group || m_display == display_table_footer_group || m_display == display_table_row || m_display == display_table_cell || m_display == display_table_caption || m_display == display_inline_block) { m_display = display_block; } } else if(el->is_root()) { // 4. Otherwise, if the element is the root element, 'display' is set according to the table below, // except that it is undefined in CSS 2.2 whether a specified value of 'list-item' becomes a // computed value of 'block' or 'list-item'. if (m_display == display_inline_table) { m_display = display_table; } else if (m_display == display_inline || m_display == display_table_row_group || m_display == display_table_column || m_display == display_table_column_group || m_display == display_table_header_group || m_display == display_table_footer_group || m_display == display_table_row || m_display == display_table_cell || m_display == display_table_caption || m_display == display_inline_block || m_display == display_list_item) { m_display = display_block; } } } // 5. Otherwise, the remaining 'display' property values apply as specified. const css_length _auto = css_length::predef_value(0); const css_length none = _auto, normal = _auto; m_css_width = el->get_property(_width_, false, _auto, offset(m_css_width)); m_css_height = el->get_property(_height_, false, _auto, offset(m_css_height)); m_css_min_width = el->get_property(_min_width_, false, _auto, offset(m_css_min_width)); m_css_min_height = el->get_property(_min_height_, false, _auto, offset(m_css_min_height)); m_css_max_width = el->get_property(_max_width_, false, none, offset(m_css_max_width)); m_css_max_height = el->get_property(_max_height_, false, none, offset(m_css_max_height)); doc->cvt_units(m_css_width, m_font_metrics, 0); doc->cvt_units(m_css_height, m_font_metrics, 0); doc->cvt_units(m_css_min_width, m_font_metrics, 0); doc->cvt_units(m_css_min_height, m_font_metrics, 0); doc->cvt_units(m_css_max_width, m_font_metrics, 0); doc->cvt_units(m_css_max_height, m_font_metrics, 0); m_css_margins.left = el->get_property(_margin_left_, false, 0, offset(m_css_margins.left)); m_css_margins.right = el->get_property(_margin_right_, false, 0, offset(m_css_margins.right)); m_css_margins.top = el->get_property(_margin_top_, false, 0, offset(m_css_margins.top)); m_css_margins.bottom = el->get_property(_margin_bottom_, false, 0, offset(m_css_margins.bottom)); doc->cvt_units(m_css_margins.left, m_font_metrics, 0); doc->cvt_units(m_css_margins.right, m_font_metrics, 0); doc->cvt_units(m_css_margins.top, m_font_metrics, 0); doc->cvt_units(m_css_margins.bottom, m_font_metrics, 0); m_css_padding.left = el->get_property(_padding_left_, false, 0, offset(m_css_padding.left)); m_css_padding.right = el->get_property(_padding_right_, false, 0, offset(m_css_padding.right)); m_css_padding.top = el->get_property(_padding_top_, false, 0, offset(m_css_padding.top)); m_css_padding.bottom = el->get_property(_padding_bottom_, false, 0, offset(m_css_padding.bottom)); doc->cvt_units(m_css_padding.left, m_font_metrics, 0); doc->cvt_units(m_css_padding.right, m_font_metrics, 0); doc->cvt_units(m_css_padding.top, m_font_metrics, 0); doc->cvt_units(m_css_padding.bottom, m_font_metrics, 0); m_css_borders.left.color = get_color_property(el, _border_left_color_, false, m_color, offset(m_css_borders.left.color)); m_css_borders.right.color = get_color_property(el, _border_right_color_, false, m_color, offset(m_css_borders.right.color)); m_css_borders.top.color = get_color_property(el, _border_top_color_, false, m_color, offset(m_css_borders.top.color)); m_css_borders.bottom.color = get_color_property(el, _border_bottom_color_, false, m_color, offset(m_css_borders.bottom.color)); m_css_borders.left.style = (border_style) el->get_property(_border_left_style_, false, border_style_none, offset(m_css_borders.left.style)); m_css_borders.right.style = (border_style) el->get_property(_border_right_style_, false, border_style_none, offset(m_css_borders.right.style)); m_css_borders.top.style = (border_style) el->get_property(_border_top_style_, false, border_style_none, offset(m_css_borders.top.style)); m_css_borders.bottom.style = (border_style) el->get_property(_border_bottom_style_, false, border_style_none, offset(m_css_borders.bottom.style)); m_css_borders.left.width = el->get_property(_border_left_width_, false, border_width_medium_value, offset(m_css_borders.left.width)); m_css_borders.right.width = el->get_property(_border_right_width_, false, border_width_medium_value, offset(m_css_borders.right.width)); m_css_borders.top.width = el->get_property(_border_top_width_, false, border_width_medium_value, offset(m_css_borders.top.width)); m_css_borders.bottom.width = el->get_property(_border_bottom_width_, false, border_width_medium_value, offset(m_css_borders.bottom.width)); if (m_css_borders.left.style == border_style_none || m_css_borders.left.style == border_style_hidden) m_css_borders.left.width = 0; if (m_css_borders.right.style == border_style_none || m_css_borders.right.style == border_style_hidden) m_css_borders.right.width = 0; if (m_css_borders.top.style == border_style_none || m_css_borders.top.style == border_style_hidden) m_css_borders.top.width = 0; if (m_css_borders.bottom.style == border_style_none || m_css_borders.bottom.style == border_style_hidden) m_css_borders.bottom.width = 0; doc->cvt_units(m_css_borders.left.width, m_font_metrics, 0); doc->cvt_units(m_css_borders.right.width, m_font_metrics, 0); doc->cvt_units(m_css_borders.top.width, m_font_metrics, 0); doc->cvt_units(m_css_borders.bottom.width, m_font_metrics, 0); m_css_borders.radius.top_left_x = el->get_property(_border_top_left_radius_x_, false, 0, offset(m_css_borders.radius.top_left_x)); m_css_borders.radius.top_left_y = el->get_property(_border_top_left_radius_y_, false, 0, offset(m_css_borders.radius.top_left_y)); m_css_borders.radius.top_right_x = el->get_property(_border_top_right_radius_x_, false, 0, offset(m_css_borders.radius.top_right_x)); m_css_borders.radius.top_right_y = el->get_property(_border_top_right_radius_y_, false, 0, offset(m_css_borders.radius.top_right_y)); m_css_borders.radius.bottom_left_x = el->get_property(_border_bottom_left_radius_x_, false, 0, offset(m_css_borders.radius.bottom_left_x)); m_css_borders.radius.bottom_left_y = el->get_property(_border_bottom_left_radius_y_, false, 0, offset(m_css_borders.radius.bottom_left_y)); m_css_borders.radius.bottom_right_x = el->get_property(_border_bottom_right_radius_x_, false, 0, offset(m_css_borders.radius.bottom_right_x)); m_css_borders.radius.bottom_right_y = el->get_property(_border_bottom_right_radius_y_, false, 0, offset(m_css_borders.radius.bottom_right_y)); doc->cvt_units( m_css_borders.radius.top_left_x, m_font_metrics, 0); doc->cvt_units( m_css_borders.radius.top_left_y, m_font_metrics, 0); doc->cvt_units( m_css_borders.radius.top_right_x, m_font_metrics, 0); doc->cvt_units( m_css_borders.radius.top_right_y, m_font_metrics, 0); doc->cvt_units( m_css_borders.radius.bottom_left_x, m_font_metrics, 0); doc->cvt_units( m_css_borders.radius.bottom_left_y, m_font_metrics, 0); doc->cvt_units( m_css_borders.radius.bottom_right_x, m_font_metrics, 0); doc->cvt_units( m_css_borders.radius.bottom_right_y, m_font_metrics, 0); m_border_collapse = (border_collapse) el->get_property(_border_collapse_, true, border_collapse_separate, offset(m_border_collapse)); m_css_border_spacing_x = el->get_property(__litehtml_border_spacing_x_, true, 0, offset(m_css_border_spacing_x)); m_css_border_spacing_y = el->get_property(__litehtml_border_spacing_y_, true, 0, offset(m_css_border_spacing_y)); doc->cvt_units(m_css_border_spacing_x, m_font_metrics, 0); doc->cvt_units(m_css_border_spacing_y, m_font_metrics, 0); m_css_offsets.left = el->get_property(_left_, false, _auto, offset(m_css_offsets.left)); m_css_offsets.right = el->get_property(_right_, false, _auto, offset(m_css_offsets.right)); m_css_offsets.top = el->get_property(_top_, false, _auto, offset(m_css_offsets.top)); m_css_offsets.bottom = el->get_property(_bottom_,false, _auto, offset(m_css_offsets.bottom)); doc->cvt_units(m_css_offsets.left, m_font_metrics, 0); doc->cvt_units(m_css_offsets.right, m_font_metrics, 0); doc->cvt_units(m_css_offsets.top, m_font_metrics, 0); doc->cvt_units(m_css_offsets.bottom, m_font_metrics, 0); m_z_index = el->get_property(_z_index_, false, _auto, offset(m_z_index)); m_content = el->get_property(_content_, false, "", offset(m_content)); m_cursor = el->get_property(_cursor_, true, "auto", offset(m_cursor)); m_css_text_indent = el->get_property(_text_indent_, true, 0, offset(m_css_text_indent)); doc->cvt_units(m_css_text_indent, m_font_metrics, 0); m_css_line_height = el->get_property(_line_height_, true, normal, offset(m_css_line_height)); if(m_css_line_height.is_predefined()) { m_line_height = m_font_metrics.height; } else if(m_css_line_height.units() == css_units_none) { m_line_height = (int) std::nearbyint(m_css_line_height.val() * font_size); } else { m_line_height = doc->to_pixels(m_css_line_height, m_font_metrics, m_font_metrics.font_size); m_css_line_height = (float) m_line_height; } m_list_style_type = (list_style_type) el->get_property(_list_style_type_, true, list_style_type_disc, offset(m_list_style_type)); m_list_style_position = (list_style_position) el->get_property(_list_style_position_, true, list_style_position_outside, offset(m_list_style_position)); m_list_style_image = el->get_property(_list_style_image_, true, "", offset(m_list_style_image)); if (!m_list_style_image.empty()) { m_list_style_image_baseurl = el->get_property(_list_style_image_baseurl_, true, "", offset(m_list_style_image_baseurl)); doc->container()->load_image(m_list_style_image.c_str(), m_list_style_image_baseurl.c_str(), true); } m_order = el->get_property(_order_, false, 0, offset(m_order)); compute_background(el, doc); compute_flex(el, doc); } // used for all color properties except `color` (color:currentcolor is converted to color:inherit during parsing) litehtml::web_color litehtml::css_properties::get_color_property(const html_tag* el, string_id name, bool inherited, web_color default_value, uint_ptr member_offset) const { web_color color = el->get_property(name, inherited, default_value, member_offset); if (color.is_current_color) color = m_color; return color; } static const int font_size_table[8][7] = { { 9, 9, 9, 9, 11, 14, 18}, { 9, 9, 9, 10, 12, 15, 20}, { 9, 9, 9, 11, 13, 17, 22}, { 9, 9, 10, 12, 14, 18, 24}, { 9, 9, 10, 13, 16, 20, 26}, { 9, 9, 11, 14, 17, 21, 28}, { 9, 10, 12, 15, 17, 23, 30}, { 9, 10, 13, 16, 18, 24, 32} }; void litehtml::css_properties::compute_font(const html_tag* el, const document::ptr& doc) { // initialize font size css_length sz = el->get_property(_font_size_, true, css_length::predef_value(font_size_medium), offset(m_font_size)); int parent_sz = 0; int doc_font_size = doc->container()->get_default_font_size(); element::ptr el_parent = el->parent(); if (el_parent) { parent_sz = el_parent->css().get_font_size(); } else { parent_sz = doc_font_size; } int font_size = parent_sz; if(sz.is_predefined()) { int idx_in_table = doc_font_size - 9; if(idx_in_table >= 0 && idx_in_table <= 7) { if(sz.predef() >= font_size_xx_small && sz.predef() <= font_size_xx_large) { font_size = font_size_table[idx_in_table][sz.predef()]; } else if(sz.predef() == font_size_smaller) { font_size = (int) (parent_sz / 1.2); } else if(sz.predef() == font_size_larger) { font_size = (int) (parent_sz * 1.2); } else { font_size = parent_sz; } } else { switch(sz.predef()) { case font_size_xx_small: font_size = doc_font_size * 3 / 5; break; case font_size_x_small: font_size = doc_font_size * 3 / 4; break; case font_size_small: font_size = doc_font_size * 8 / 9; break; case font_size_large: font_size = doc_font_size * 6 / 5; break; case font_size_x_large: font_size = doc_font_size * 3 / 2; break; case font_size_xx_large: font_size = doc_font_size * 2; break; case font_size_smaller: font_size = (int) (parent_sz / 1.2); break; case font_size_larger: font_size = (int) (parent_sz * 1.2); break; default: font_size = parent_sz; break; } } } else { if(sz.units() == css_units_percentage) { font_size = sz.calc_percent(parent_sz); } else { font_metrics fm; fm.x_height = fm.font_size = parent_sz; font_size = doc->to_pixels(sz, fm, 0); } } m_font_size = (float)font_size; // initialize font m_font_family = el->get_property( _font_family_, true, doc->container()->get_default_font_name(), offset(m_font_family)); m_font_weight = el->get_property(_font_weight_, true, css_length::predef_value(font_weight_normal), offset(m_font_weight)); m_font_style = (font_style) el->get_property( _font_style_, true, font_style_normal, offset(m_font_style)); m_text_decoration = el->get_property( _text_decoration_, true, "none", offset(m_text_decoration)); m_font = doc->get_font( m_font_family.c_str(), font_size, m_font_weight.is_predefined() ? index_value(m_font_weight.predef(), font_weight_strings).c_str() : std::to_string(m_font_weight.val()).c_str(), index_value(m_font_style, font_style_strings).c_str(), m_text_decoration.c_str(), &m_font_metrics); } void litehtml::css_properties::compute_background(const html_tag* el, const document::ptr& doc) { m_bg.m_color = get_color_property(el, _background_color_, false, web_color::transparent, offset(m_bg.m_color)); const css_size auto_auto(css_length::predef_value(background_size_auto), css_length::predef_value(background_size_auto)); m_bg.m_position_x = el->get_property(_background_position_x_, false, { css_length(0, css_units_percentage) }, offset(m_bg.m_position_x)); m_bg.m_position_y = el->get_property(_background_position_y_, false, { css_length(0, css_units_percentage) }, offset(m_bg.m_position_y)); m_bg.m_size = el->get_property (_background_size_, false, { auto_auto }, offset(m_bg.m_size)); for (auto& x : m_bg.m_position_x) doc->cvt_units(x, m_font_metrics, 0); for (auto& y : m_bg.m_position_y) doc->cvt_units(y, m_font_metrics, 0); for (auto& size : m_bg.m_size) { doc->cvt_units(size.width, m_font_metrics, 0); doc->cvt_units(size.height, m_font_metrics, 0); } m_bg.m_attachment = el->get_property(_background_attachment_, false, { background_attachment_scroll }, offset(m_bg.m_attachment)); m_bg.m_repeat = el->get_property(_background_repeat_, false, { background_repeat_repeat }, offset(m_bg.m_repeat)); m_bg.m_clip = el->get_property(_background_clip_, false, { background_box_border }, offset(m_bg.m_clip)); m_bg.m_origin = el->get_property(_background_origin_, false, { background_box_padding }, offset(m_bg.m_origin)); m_bg.m_image = el->get_property>(_background_image_, false, {{}}, offset(m_bg.m_image)); m_bg.m_baseurl = el->get_property(_background_image_baseurl_, false, "", offset(m_bg.m_baseurl)); for (auto& image : m_bg.m_image) { switch (image.type) { case image::type_none: break; case image::type_url: if (!image.url.empty()) { doc->container()->load_image(image.url.c_str(), m_bg.m_baseurl.c_str(), true); } break; case image::type_gradient: for(auto& item : image.m_gradient.m_colors) { if (item.length) doc->cvt_units(*item.length, m_font_metrics, 0); } break; } } } void litehtml::css_properties::compute_flex(const html_tag* el, const document::ptr& doc) { if (m_display == display_flex || m_display == display_inline_flex) { m_flex_direction = (flex_direction) el->get_property(_flex_direction_, false, flex_direction_row, offset(m_flex_direction)); m_flex_wrap = (flex_wrap) el->get_property(_flex_wrap_, false, flex_wrap_nowrap, offset(m_flex_wrap)); m_flex_justify_content = (flex_justify_content) el->get_property(_justify_content_, false, flex_justify_content_flex_start, offset(m_flex_justify_content)); m_flex_align_items = (flex_align_items) el->get_property(_align_items_, false, flex_align_items_normal, offset(m_flex_align_items)); m_flex_align_content = (flex_align_content) el->get_property(_align_content_, false, flex_align_content_stretch, offset(m_flex_align_content)); } m_flex_align_self = (flex_align_items) el->get_property(_align_self_, false, flex_align_items_auto, offset(m_flex_align_self)); auto parent = el->parent(); if (parent && (parent->css().m_display == display_flex || parent->css().m_display == display_inline_flex)) { m_flex_grow = el->get_property(_flex_grow_, false, 0, offset(m_flex_grow)); m_flex_shrink = el->get_property(_flex_shrink_, false, 1, offset(m_flex_shrink)); m_flex_basis = el->get_property(_flex_basis_, false, css_length::predef_value(flex_basis_auto), offset(m_flex_basis)); if(!m_flex_basis.is_predefined() && m_flex_basis.units() == css_units_none && m_flex_basis.val() != 0) { // flex-basis property must contain units m_flex_basis.predef(flex_basis_auto); } doc->cvt_units(m_flex_basis, m_font_metrics, 0); if(m_display == display_inline || m_display == display_inline_block) { m_display = display_block; } else if(m_display == display_inline_table) { m_display = display_table; } else if(m_display == display_inline_flex) { m_display = display_flex; } } } std::vector> litehtml::css_properties::dump_get_attrs() { std::vector> ret; ret.emplace_back("display", index_value(m_display, style_display_strings)); ret.emplace_back("el_position", index_value(m_el_position, element_position_strings)); ret.emplace_back("text_align", index_value(m_text_align, text_align_strings)); ret.emplace_back("font_size", m_font_size.to_string()); ret.emplace_back("overflow", index_value(m_overflow, overflow_strings)); ret.emplace_back("white_space", index_value(m_white_space, white_space_strings)); ret.emplace_back("visibility", index_value(m_visibility, visibility_strings)); ret.emplace_back("box_sizing", index_value(m_box_sizing, box_sizing_strings)); ret.emplace_back("z_index", m_z_index.to_string()); ret.emplace_back("vertical_align", index_value(m_vertical_align, vertical_align_strings)); ret.emplace_back("float", index_value(m_float, element_float_strings)); ret.emplace_back("clear", index_value(m_clear, element_clear_strings)); ret.emplace_back("margins", m_css_margins.to_string()); ret.emplace_back("padding", m_css_padding.to_string()); ret.emplace_back("borders", m_css_borders.to_string()); ret.emplace_back("width", m_css_width.to_string()); ret.emplace_back("height", m_css_height.to_string()); ret.emplace_back("min_width", m_css_min_width.to_string()); ret.emplace_back("min_height", m_css_min_width.to_string()); ret.emplace_back("max_width", m_css_max_width.to_string()); ret.emplace_back("max_height", m_css_max_width.to_string()); ret.emplace_back("offsets", m_css_offsets.to_string()); ret.emplace_back("text_indent", m_css_text_indent.to_string()); ret.emplace_back("line_height", std::to_string(m_line_height)); ret.emplace_back("list_style_type", index_value(m_list_style_type, list_style_type_strings)); ret.emplace_back("list_style_position", index_value(m_list_style_position, list_style_position_strings)); ret.emplace_back("border_spacing_x", m_css_border_spacing_x.to_string()); ret.emplace_back("border_spacing_y", m_css_border_spacing_y.to_string()); return ret; }