commit 2ab815a9115fbad6b25f07c7e3b67ba1f79c4257 parent 357c5342dd3e36a8a629929918dda7196e3f906d Author: Jonathan Boeing <jonathan@claws-mail.org> Date: Sun, 24 Mar 2024 16:48:14 -0700 Update to LiteHTML v0.9 Diffstat:
35 files changed, 2425 insertions(+), 383 deletions(-)
diff --git a/src/plugins/litehtml_viewer/container_linux.cpp b/src/plugins/litehtml_viewer/container_linux.cpp @@ -382,6 +382,8 @@ void container_linux::draw_background( litehtml::uint_ptr hdc, const std::vector { const auto& bg = bgvec[i]; + if(bg.image_size.height == 0 || bg.image_size.width == 0) continue; + cairo_rectangle(cr, bg.clip_box.x, bg.clip_box.y, bg.clip_box.width, bg.clip_box.height); cairo_clip(cr); @@ -513,57 +515,54 @@ void container_linux::draw_borders(litehtml::uint_ptr hdc, const litehtml::borde { set_color(cr, borders.right.color); - double r_top = borders.radius.top_right_x; - double r_bottom = borders.radius.bottom_right_x; - - if(r_top) + if(borders.radius.top_right_x && borders.radius.top_right_y) { double end_angle = 2 * M_PI; double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_top / (double) bdr_right + 1); add_path_arc(cr, - draw_pos.right() - r_top, - draw_pos.top() + r_top, - r_top - bdr_right, - r_top - bdr_right + (bdr_right - bdr_top), - end_angle, - start_angle, true); + draw_pos.right() - borders.radius.top_right_x, + draw_pos.top() + borders.radius.top_right_y, + borders.radius.top_right_x - bdr_right, + borders.radius.top_right_y - bdr_right + (bdr_right - bdr_top), + end_angle, + start_angle, true); add_path_arc(cr, - draw_pos.right() - r_top, - draw_pos.top() + r_top, - r_top, - r_top, - start_angle, - end_angle, false); + draw_pos.right() - borders.radius.top_right_x, + draw_pos.top() + borders.radius.top_right_y, + borders.radius.top_right_x, + borders.radius.top_right_y, + start_angle, + end_angle, false); } else { cairo_move_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top); cairo_line_to(cr, draw_pos.right(), draw_pos.top()); } - if(r_bottom) + if(borders.radius.bottom_right_x && borders.radius.bottom_right_y) { - cairo_line_to(cr, draw_pos.right(), draw_pos.bottom() - r_bottom); + cairo_line_to(cr, draw_pos.right(), draw_pos.bottom() - borders.radius.bottom_right_y); double start_angle = 0; double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_bottom / (double) bdr_right + 1); add_path_arc(cr, - draw_pos.right() - r_bottom, - draw_pos.bottom() - r_bottom, - r_bottom, - r_bottom, - start_angle, - end_angle, false); + draw_pos.right() - borders.radius.bottom_right_x, + draw_pos.bottom() - borders.radius.bottom_right_y, + borders.radius.bottom_right_x, + borders.radius.bottom_right_y, + start_angle, + end_angle, false); add_path_arc(cr, - draw_pos.right() - r_bottom, - draw_pos.bottom() - r_bottom, - r_bottom - bdr_right, - r_bottom - bdr_right + (bdr_right - bdr_bottom), - end_angle, - start_angle, true); + draw_pos.right() - borders.radius.bottom_right_x, + draw_pos.bottom() - borders.radius.bottom_right_y, + borders.radius.bottom_right_x - bdr_right, + borders.radius.bottom_right_y - bdr_right + (bdr_right - bdr_bottom), + end_angle, + start_angle, true); } else { cairo_line_to(cr, draw_pos.right(), draw_pos.bottom()); @@ -578,57 +577,54 @@ void container_linux::draw_borders(litehtml::uint_ptr hdc, const litehtml::borde { set_color(cr, borders.bottom.color); - double r_left = borders.radius.bottom_left_x; - double r_right = borders.radius.bottom_right_x; - - if(r_left) + if(borders.radius.bottom_left_x && borders.radius.bottom_left_y) { double start_angle = M_PI / 2.0; double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_left / (double) bdr_bottom + 1); add_path_arc(cr, - draw_pos.left() + r_left, - draw_pos.bottom() - r_left, - r_left - bdr_bottom + (bdr_bottom - bdr_left), - r_left - bdr_bottom, - start_angle, - end_angle, false); + draw_pos.left() + borders.radius.bottom_left_x, + draw_pos.bottom() - borders.radius.bottom_left_y, + borders.radius.bottom_left_x - bdr_bottom + (bdr_bottom - bdr_left), + borders.radius.bottom_left_y - bdr_bottom, + start_angle, + end_angle, false); add_path_arc(cr, - draw_pos.left() + r_left, - draw_pos.bottom() - r_left, - r_left, - r_left, - end_angle, - start_angle, true); + draw_pos.left() + borders.radius.bottom_left_x, + draw_pos.bottom() - borders.radius.bottom_left_y, + borders.radius.bottom_left_x, + borders.radius.bottom_left_y, + end_angle, + start_angle, true); } else { cairo_move_to(cr, draw_pos.left(), draw_pos.bottom()); cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom); } - if(r_right) + if(borders.radius.bottom_right_x && borders.radius.bottom_right_y) { - cairo_line_to(cr, draw_pos.right() - r_right, draw_pos.bottom()); + cairo_line_to(cr, draw_pos.right() - borders.radius.bottom_right_x, draw_pos.bottom()); double end_angle = M_PI / 2.0; double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_right / (double) bdr_bottom + 1); add_path_arc(cr, - draw_pos.right() - r_right, - draw_pos.bottom() - r_right, - r_right, - r_right, - end_angle, - start_angle, true); + draw_pos.right() - borders.radius.bottom_right_x, + draw_pos.bottom() - borders.radius.bottom_right_y, + borders.radius.bottom_right_x, + borders.radius.bottom_right_y, + end_angle, + start_angle, true); add_path_arc(cr, - draw_pos.right() - r_right, - draw_pos.bottom() - r_right, - r_right - bdr_bottom + (bdr_bottom - bdr_right), - r_right - bdr_bottom, - start_angle, - end_angle, false); + draw_pos.right() - borders.radius.bottom_right_x, + draw_pos.bottom() - borders.radius.bottom_right_y, + borders.radius.bottom_right_x - bdr_bottom + (bdr_bottom - bdr_right), + borders.radius.bottom_right_y - bdr_bottom, + start_angle, + end_angle, false); } else { cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom); @@ -643,57 +639,54 @@ void container_linux::draw_borders(litehtml::uint_ptr hdc, const litehtml::borde { set_color(cr, borders.top.color); - double r_left = borders.radius.top_left_x; - double r_right = borders.radius.top_right_x; - - if(r_left) + if(borders.radius.top_left_x && borders.radius.top_left_y) { double end_angle = M_PI * 3.0 / 2.0; double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_left / (double) bdr_top + 1); add_path_arc(cr, - draw_pos.left() + r_left, - draw_pos.top() + r_left, - r_left, - r_left, - end_angle, - start_angle, true); + draw_pos.left() + borders.radius.top_left_x, + draw_pos.top() + borders.radius.top_left_y, + borders.radius.top_left_x, + borders.radius.top_left_y, + end_angle, + start_angle, true); add_path_arc(cr, - draw_pos.left() + r_left, - draw_pos.top() + r_left, - r_left - bdr_top + (bdr_top - bdr_left), - r_left - bdr_top, - start_angle, - end_angle, false); + draw_pos.left() + borders.radius.top_left_x, + draw_pos.top() + borders.radius.top_left_y, + borders.radius.top_left_x - bdr_top + (bdr_top - bdr_left), + borders.radius.top_left_y - bdr_top, + start_angle, + end_angle, false); } else { cairo_move_to(cr, draw_pos.left(), draw_pos.top()); cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top); } - if(r_right) + if(borders.radius.top_right_x && borders.radius.top_right_y) { - cairo_line_to(cr, draw_pos.right() - r_right, draw_pos.top() + bdr_top); + cairo_line_to(cr, draw_pos.right() - borders.radius.top_right_x, draw_pos.top() + bdr_top); double start_angle = M_PI * 3.0 / 2.0; double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_right / (double) bdr_top + 1); add_path_arc(cr, - draw_pos.right() - r_right, - draw_pos.top() + r_right, - r_right - bdr_top + (bdr_top - bdr_right), - r_right - bdr_top, - start_angle, - end_angle, false); + draw_pos.right() - borders.radius.top_right_x, + draw_pos.top() + borders.radius.top_right_y, + borders.radius.top_right_x - bdr_top + (bdr_top - bdr_right), + borders.radius.top_right_y - bdr_top, + start_angle, + end_angle, false); add_path_arc(cr, - draw_pos.right() - r_right, - draw_pos.top() + r_right, - r_right, - r_right, - end_angle, - start_angle, true); + draw_pos.right() - borders.radius.top_right_x, + draw_pos.top() + borders.radius.top_right_y, + borders.radius.top_right_x, + borders.radius.top_right_y, + end_angle, + start_angle, true); } else { cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top); @@ -708,57 +701,54 @@ void container_linux::draw_borders(litehtml::uint_ptr hdc, const litehtml::borde { set_color(cr, borders.left.color); - double r_top = borders.radius.top_left_x; - double r_bottom = borders.radius.bottom_left_x; - - if(r_top) + if(borders.radius.top_left_x && borders.radius.top_left_y) { double start_angle = M_PI; double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_top / (double) bdr_left + 1); add_path_arc(cr, - draw_pos.left() + r_top, - draw_pos.top() + r_top, - r_top - bdr_left, - r_top - bdr_left + (bdr_left - bdr_top), - start_angle, - end_angle, false); + draw_pos.left() + borders.radius.top_left_x, + draw_pos.top() + borders.radius.top_left_y, + borders.radius.top_left_x - bdr_left, + borders.radius.top_left_y - bdr_left + (bdr_left - bdr_top), + start_angle, + end_angle, false); add_path_arc(cr, - draw_pos.left() + r_top, - draw_pos.top() + r_top, - r_top, - r_top, - end_angle, - start_angle, true); + draw_pos.left() + borders.radius.top_left_x, + draw_pos.top() + borders.radius.top_left_y, + borders.radius.top_left_x, + borders.radius.top_left_y, + end_angle, + start_angle, true); } else { cairo_move_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top); cairo_line_to(cr, draw_pos.left(), draw_pos.top()); } - if(r_bottom) + if(borders.radius.bottom_left_x && borders.radius.bottom_left_y) { - cairo_line_to(cr, draw_pos.left(), draw_pos.bottom() - r_bottom); + cairo_line_to(cr, draw_pos.left(), draw_pos.bottom() - borders.radius.bottom_left_y); double end_angle = M_PI; double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_bottom / (double) bdr_left + 1); add_path_arc(cr, - draw_pos.left() + r_bottom, - draw_pos.bottom() - r_bottom, - r_bottom, - r_bottom, - end_angle, - start_angle, true); + draw_pos.left() + borders.radius.bottom_left_x, + draw_pos.bottom() - borders.radius.bottom_left_y, + borders.radius.bottom_left_x, + borders.radius.bottom_left_y, + end_angle, + start_angle, true); add_path_arc(cr, - draw_pos.left() + r_bottom, - draw_pos.bottom() - r_bottom, - r_bottom - bdr_left, - r_bottom - bdr_left + (bdr_left - bdr_bottom), - start_angle, - end_angle, false); + draw_pos.left() + borders.radius.bottom_left_x, + draw_pos.bottom() - borders.radius.bottom_left_y, + borders.radius.bottom_left_x - bdr_left, + borders.radius.bottom_left_y - bdr_left + (bdr_left - bdr_bottom), + start_angle, + end_angle, false); } else { cairo_line_to(cr, draw_pos.left(), draw_pos.bottom()); @@ -940,9 +930,15 @@ std::shared_ptr<litehtml::element> container_linux::create_element(const char *t void container_linux::rounded_rectangle( cairo_t* cr, const litehtml::position &pos, const litehtml::border_radiuses &radius ) { cairo_new_path(cr); - if(radius.top_left_x) + if(radius.top_left_x && radius.top_left_y) { - cairo_arc(cr, pos.left() + radius.top_left_x, pos.top() + radius.top_left_x, radius.top_left_x, M_PI, M_PI * 3.0 / 2.0); + add_path_arc(cr, + pos.left() + radius.top_left_x, + pos.top() + radius.top_left_y, + radius.top_left_x, + radius.top_left_y, + M_PI, + M_PI * 3.0 / 2.0, false); } else { cairo_move_to(cr, pos.left(), pos.top()); @@ -950,23 +946,41 @@ void container_linux::rounded_rectangle( cairo_t* cr, const litehtml::position & cairo_line_to(cr, pos.right() - radius.top_right_x, pos.top()); - if(radius.top_right_x) + if(radius.top_right_x && radius.top_right_y) { - cairo_arc(cr, pos.right() - radius.top_right_x, pos.top() + radius.top_right_x, radius.top_right_x, M_PI * 3.0 / 2.0, 2.0 * M_PI); + add_path_arc(cr, + pos.right() - radius.top_right_x, + pos.top() + radius.top_right_y, + radius.top_right_x, + radius.top_right_y, + M_PI * 3.0 / 2.0, + 2.0 * M_PI, false); } cairo_line_to(cr, pos.right(), pos.bottom() - radius.bottom_right_x); - if(radius.bottom_right_x) + if(radius.bottom_right_x && radius.bottom_right_y) { - cairo_arc(cr, pos.right() - radius.bottom_right_x, pos.bottom() - radius.bottom_right_x, radius.bottom_right_x, 0, M_PI / 2.0); + add_path_arc(cr, + pos.right() - radius.bottom_right_x, + pos.bottom() - radius.bottom_right_y, + radius.bottom_right_x, + radius.bottom_right_y, + 0, + M_PI / 2.0, false); } cairo_line_to(cr, pos.left() - radius.bottom_left_x, pos.bottom()); - if(radius.bottom_left_x) + if(radius.bottom_left_x && radius.bottom_left_y) { - cairo_arc(cr, pos.left() + radius.bottom_left_x, pos.bottom() - radius.bottom_left_x, radius.bottom_left_x, M_PI / 2.0, M_PI); + add_path_arc(cr, + pos.left() + radius.bottom_left_x, + pos.bottom() - radius.bottom_left_y, + radius.bottom_left_x, + radius.bottom_left_y, + M_PI / 2.0, + M_PI, false); } } diff --git a/src/plugins/litehtml_viewer/litehtml/Makefile.am b/src/plugins/litehtml_viewer/litehtml/Makefile.am @@ -76,6 +76,10 @@ liblitehtml_la_SOURCES = \ el_title.h \ el_tr.cpp \ el_tr.h \ + flex_item.cpp \ + flex_item.h \ + flex_line.cpp \ + flex_line.h \ formatting_context.cpp \ formatting_context.h \ html.cpp \ @@ -104,7 +108,6 @@ liblitehtml_la_SOURCES = \ render_image.h \ render_inline_context.cpp \ render_inline_context.h \ - render_inline.cpp \ render_inline.h \ render_item.cpp \ render_item.h \ diff --git a/src/plugins/litehtml_viewer/litehtml/borders.h b/src/plugins/litehtml_viewer/litehtml/borders.h @@ -157,6 +157,35 @@ namespace litehtml if (bottom_left_x < 0) bottom_left_x = 0; if (bottom_left_y < 0) bottom_left_y = 0; } + void fix_values(int width, int height) + { + fix_values(); + int half_width = width / 2; + int half_height = height / 2; + auto fix_one = [&](int& radii_x, int& radii_y) + { + double factor = std::min((double) half_width / (double) radii_x, (double) half_height / (double) radii_y); + radii_x = (int) ((double) radii_x * factor); + radii_y = (int) ((double) radii_y * factor); + }; + + if(top_left_x > half_width || top_left_y > half_height) + { + fix_one(top_left_x, top_left_y); + } + if(top_right_x > half_width || top_right_y > half_height) + { + fix_one(top_right_x, top_right_y); + } + if(bottom_right_x > half_width || bottom_right_y > half_height) + { + fix_one(bottom_right_x, bottom_right_y); + } + if(bottom_left_x > half_width || bottom_left_y > half_height) + { + fix_one(bottom_left_x, bottom_left_y); + } + } }; struct css_border_radius @@ -213,6 +242,7 @@ namespace litehtml ret.top_right_y = top_right_y.calc_percent(height); ret.bottom_right_x = bottom_right_x.calc_percent(width); ret.bottom_right_y = bottom_right_y.calc_percent(height); + ret.fix_values(width, height); return ret; } }; diff --git a/src/plugins/litehtml_viewer/litehtml/css_properties.cpp b/src/plugins/litehtml_viewer/litehtml/css_properties.cpp @@ -240,6 +240,8 @@ void litehtml::css_properties::compute(const element* el, const document::ptr& d doc->container()->load_image(m_list_style_image.c_str(), m_list_style_image_baseurl.c_str(), true); } + m_order = el->get_int_property(_order_, false, 0, offset(m_order)); + compute_background(el, doc); compute_flex(el, doc); } @@ -391,22 +393,27 @@ void litehtml::css_properties::compute_background(const element* el, const docum void litehtml::css_properties::compute_flex(const element* el, const document::ptr& doc) { - if (m_display == display_flex) + if (m_display == display_flex || m_display == display_inline_flex) { m_flex_direction = (flex_direction) el->get_enum_property(_flex_direction_, false, flex_direction_row, offset(m_flex_direction)); m_flex_wrap = (flex_wrap) el->get_enum_property(_flex_wrap_, false, flex_wrap_nowrap, offset(m_flex_wrap)); m_flex_justify_content = (flex_justify_content) el->get_enum_property(_justify_content_, false, flex_justify_content_flex_start, offset(m_flex_justify_content)); - m_flex_align_items = (flex_align_items) el->get_enum_property(_align_items_, false, flex_align_items_stretch, offset(m_flex_align_items)); + m_flex_align_items = (flex_align_items) el->get_enum_property(_align_items_, false, flex_align_items_flex_normal, offset(m_flex_align_items)); m_flex_align_content = (flex_align_content) el->get_enum_property(_align_content_, false, flex_align_content_stretch, offset(m_flex_align_content)); } + m_flex_align_self = (flex_align_items) el->get_enum_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) + if (parent && (parent->css().m_display == display_flex || parent->css().m_display == display_inline_flex)) { m_flex_grow = el->get_number_property(_flex_grow_, false, 0, offset(m_flex_grow)); m_flex_shrink = el->get_number_property(_flex_shrink_, false, 1, offset(m_flex_shrink)); - m_flex_align_self = (flex_align_self) el->get_enum_property(_align_self_, false, flex_align_self_auto, offset(m_flex_align_self)); - m_flex_basis = el->get_length_property(_flex_shrink_, false, css_length::predef_value(flex_basis_auto), offset(m_flex_basis)); + m_flex_basis = el->get_length_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, get_font_size()); if(m_display == display_inline || m_display == display_inline_block) { diff --git a/src/plugins/litehtml_viewer/litehtml/css_properties.h b/src/plugins/litehtml_viewer/litehtml/css_properties.h @@ -67,11 +67,13 @@ namespace litehtml flex_wrap m_flex_wrap; flex_justify_content m_flex_justify_content; flex_align_items m_flex_align_items; - flex_align_self m_flex_align_self; + flex_align_items m_flex_align_self; flex_align_content m_flex_align_content; caption_side m_caption_side; + int m_order; + private: void compute_font(const element* el, const std::shared_ptr<document>& doc); void compute_background(const element* el, const std::shared_ptr<document>& doc); @@ -119,8 +121,9 @@ namespace litehtml m_flex_wrap(flex_wrap_nowrap), m_flex_justify_content(flex_justify_content_flex_start), m_flex_align_items(flex_align_items_stretch), - m_flex_align_self(flex_align_self_auto), - m_flex_align_content(flex_align_content_stretch) + m_flex_align_self(flex_align_items_auto), + m_flex_align_content(flex_align_content_stretch), + m_order(0) {} void compute(const element* el, const std::shared_ptr<document>& doc); @@ -250,8 +253,11 @@ namespace litehtml flex_wrap get_flex_wrap() const; flex_justify_content get_flex_justify_content() const; flex_align_items get_flex_align_items() const; - flex_align_self get_flex_align_self() const; + flex_align_items get_flex_align_self() const; flex_align_content get_flex_align_content() const; + + int get_order() const; + void set_order(int order); }; inline element_position css_properties::get_position() const @@ -634,7 +640,7 @@ namespace litehtml return m_flex_align_items; } - inline flex_align_self css_properties::get_flex_align_self() const + inline flex_align_items css_properties::get_flex_align_self() const { return m_flex_align_self; } @@ -653,6 +659,15 @@ namespace litehtml m_caption_side = side; } + inline int css_properties::get_order() const + { + return m_order; + } + + inline void css_properties::set_order(int order) + { + m_order = order; + } } #endif //LITEHTML_CSS_PROPERTIES_H diff --git a/src/plugins/litehtml_viewer/litehtml/el_before_after.cpp b/src/plugins/litehtml_viewer/litehtml/el_before_after.cpp @@ -133,7 +133,7 @@ void litehtml::el_before_after_base::add_text( const string& txt ) void litehtml::el_before_after_base::add_function( const string& fnc, const string& params ) { - int idx = value_index(fnc, "attr;counter;url"); + int idx = value_index(fnc, "attr;counter;counters;url"); switch(idx) { // attr @@ -155,10 +155,19 @@ void litehtml::el_before_after_base::add_function( const string& fnc, const stri break; // counter case 1: + add_text(get_counter_value(params)); break; - // url + // counters case 2: { + string_vector tokens; + split_string(params, tokens, ","); + add_text(get_counters_value(tokens)); + } + break; + // url + case 3: + { string p_url = params; trim(p_url); if(!p_url.empty()) diff --git a/src/plugins/litehtml_viewer/litehtml/element.cpp b/src/plugins/litehtml_viewer/litehtml/element.cpp @@ -274,8 +274,18 @@ element::ptr element::_add_before_after(int type, const style& style) bool element::is_block_formatting_context() const { + if(m_css.get_display() == display_block) + { + auto par = parent(); + if(par && (par->css().get_display() == display_inline_flex || par->css().get_display() == display_flex)) + { + return true; + } + } if( m_css.get_display() == display_inline_block || m_css.get_display() == display_table_cell || + m_css.get_display() == display_inline_flex || + m_css.get_display() == display_flex || m_css.get_display() == display_table_caption || is_root() || m_css.get_float() != float_none || @@ -288,6 +298,122 @@ bool element::is_block_formatting_context() const return false; } +litehtml::string litehtml::element::get_counter_value(const string& counter_name) +{ + std::map<string_id, int>::iterator i; + if (find_counter(_id(counter_name), i)) + { + return std::to_string(i->second); + } + return "0"; +} + +string litehtml::element::get_counters_value(const string_vector& parameters) +{ + string result = ""; + if (parameters.size() >= 2) { + const string_id counter_name_id = _id(parameters[0]); + string delims = parameters[1]; + litehtml::trim(delims, "\"'"); + + string_vector values; + + element::ptr current = shared_from_this(); + while (current != nullptr) + { + auto map_iterator = current->m_counter_values.find(counter_name_id); + if (map_iterator != current->m_counter_values.end()) { + values.push_back(std::to_string(map_iterator->second)); + } + current = current->parent(); + } + if (values.empty()) { + // if no counter is found, instanciate one with value '0' + shared_from_this()->m_counter_values[counter_name_id] = 0; + result = "0"; + } + else { + std::reverse(values.begin(), values.end()); + litehtml::join_string(result, values, delims); + } + } + return result; +} + + +bool litehtml::element::find_counter(const string_id& counter_name_id, std::map<string_id, int>::iterator& map_iterator) { + element::ptr current = shared_from_this(); + + while (current != nullptr) + { + map_iterator = current->m_counter_values.find(counter_name_id); + if (map_iterator != current->m_counter_values.end()) { + return true; + } + + // on each level, search previous siblings too + std::vector<element::ptr> siblings = current->get_siblings_before(); + std::reverse(siblings.begin(), siblings.end()); + for (const element::ptr& sibling : siblings) { + map_iterator = sibling->m_counter_values.find(counter_name_id); + if (map_iterator != sibling->m_counter_values.end()) { + return true; + } + } + current = current->parent(); + } + + return false; +} + +std::vector<element::ptr> litehtml::element::get_siblings_before() const +{ + std::vector<element::ptr> siblings; + if (parent() != nullptr) { + for (const element::ptr& sybling : parent()->children()) { + if (sybling == shared_from_this()) { + break; + } + siblings.push_back(sybling); + } + } + return siblings; +} + + +void litehtml::element::parse_counter_tokens(const string_vector& tokens, const int default_value, std::function<void(const string_id&, const int)> handler) const { + int pos = 0; + while (pos < tokens.size()) { + string name = tokens[pos]; + int value = default_value; + if (pos < tokens.size() - 1 && litehtml::is_number(tokens[pos + 1], 0)) { + value = atoi(tokens[pos + 1].c_str()); + pos += 2; + } + else { + pos += 1; + } + handler(_id(name), value); + } +} + +void litehtml::element::increment_counter(const string_id& counter_name_id, const int increment) +{ + std::map<string_id, int>::iterator i; + if (find_counter(counter_name_id, i)) { + i->second = i->second + increment; + } + else { + // if counter is not found, initialize one on this element + m_counter_values[counter_name_id] = increment; + } +} + +void litehtml::element::reset_counter(const string_id& counter_name_id, const int value) +{ + m_counter_values[counter_name_id] = value; +} + const background* element::get_background(bool own_only) LITEHTML_RETURN_FUNC(nullptr) void element::add_style( const style& style) LITEHTML_EMPTY_FUNC void element::select_all(const css_selector& selector, elements_list& res) LITEHTML_EMPTY_FUNC @@ -332,6 +458,7 @@ bool element::is_replaced() const LITEHTML_RETURN_FUNC(false) void element::draw(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) LITEHTML_EMPTY_FUNC void element::draw_background(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) LITEHTML_EMPTY_FUNC int element::get_enum_property (string_id name, bool inherited, int defval, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC(0) +int element::get_int_property (string_id name, bool inherited, int defval, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC(0) css_length element::get_length_property (string_id name, bool inherited, css_length defval, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC(0) web_color element::get_color_property (string_id name, bool inherited, web_color defval, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC(web_color()) string element::get_string_property (string_id name, bool inherited, const string& defval, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC("") diff --git a/src/plugins/litehtml_viewer/litehtml/element.h b/src/plugins/litehtml_viewer/litehtml/element.h @@ -35,6 +35,10 @@ namespace litehtml virtual void select_all(const css_selector& selector, elements_list& res); element::ptr _add_before_after(int type, const style& style); + + private: + std::map<string_id, int> m_counter_values; + public: explicit element(const std::shared_ptr<document>& doc); virtual ~element() = default; @@ -99,6 +103,7 @@ namespace litehtml virtual void draw(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item>& ri); virtual void draw_background(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri); virtual int get_enum_property (string_id name, bool inherited, int default_value, uint_ptr css_properties_member_offset) const; + virtual int get_int_property (string_id name, bool inherited, int default_value, uint_ptr css_properties_member_offset) const; virtual css_length get_length_property(string_id name, bool inherited, css_length default_value, uint_ptr css_properties_member_offset) const; virtual web_color get_color_property (string_id name, bool inherited, web_color default_value, uint_ptr css_properties_member_offset) const; virtual string get_string_property(string_id name, bool inherited, const string& default_value, uint_ptr css_properties_member_offset) const; @@ -142,6 +147,16 @@ namespace litehtml { return _add_before_after(1, style); } + + string get_counter_value(const string& counter_name); + string get_counters_value(const string_vector& parameters); + void increment_counter(const string_id& counter_name_id, const int increment = 1); + void reset_counter(const string_id& counter_name_id, const int value = 0); + + private: + std::vector<element::ptr> get_siblings_before() const; + bool find_counter(const string_id& counter_name_id, std::map<string_id, int>::iterator& map_iterator); + void parse_counter_tokens(const string_vector& tokens, const int default_value, std::function<void(const string_id&, const int)> handler) const; }; ////////////////////////////////////////////////////////////////////////// diff --git a/src/plugins/litehtml_viewer/litehtml/flex_item.cpp b/src/plugins/litehtml_viewer/litehtml/flex_item.cpp @@ -0,0 +1,488 @@ +#include "html.h" +#include "flex_item.h" +#include "render_item.h" +#include "flex_line.h" +#include <cmath> + +void litehtml::flex_item::init(const litehtml::containing_block_context &self_size, + litehtml::formatting_context *fmt_ctx, flex_align_items align_items) +{ + grow = (int) std::nearbyint(el->css().get_flex_grow() * 1000.0); + // Negative numbers are invalid. + // https://www.w3.org/TR/css-flexbox-1/#valdef-flex-grow-number + if(grow < 0) grow = 0; + + shrink = (int) std::nearbyint(el->css().get_flex_shrink() * 1000.0); + // Negative numbers are invalid. + // https://www.w3.org/TR/css-flexbox-1/#valdef-flex-shrink-number + if(shrink < 0) shrink = 1000; + + el->calc_outlines(self_size.render_width); + order = el->css().get_order(); + + direction_specific_init(self_size, fmt_ctx); + + if (el->css().get_flex_align_self() == flex_align_items_auto) + { + align = align_items; + } else + { + align = el->css().get_flex_align_self(); + } + main_size = base_size; + scaled_flex_shrink_factor = base_size * shrink; + frozen = false; +} + +void litehtml::flex_item::place(flex_line &ln, int main_pos, + const containing_block_context &self_size, + formatting_context *fmt_ctx) +{ + apply_main_auto_margins(); + set_main_position(main_pos); + if(!apply_cross_auto_margins(ln.cross_size)) + { + switch (align & 0xFF) + { + case flex_align_items_baseline: + align_baseline(ln, self_size, fmt_ctx); + break; + case flex_align_items_flex_end: + if(ln.reverse_cross) + { + set_cross_position(ln.cross_start); + break; /// If cross axis is reversed position item from start + } + case flex_align_items_end: + set_cross_position(ln.cross_start + ln.cross_size - get_el_cross_size()); + break; + case flex_align_items_center: + set_cross_position(ln.cross_start + ln.cross_size / 2 - get_el_cross_size() / 2); + break; + case flex_align_items_flex_start: + if(ln.reverse_cross) /// If cross axis is reversed position item from end + { + set_cross_position(ln.cross_start + ln.cross_size - get_el_cross_size()); + break; + } + case flex_align_items_start: + set_cross_position(ln.cross_start); + break; + default: + align_stretch(ln, self_size, fmt_ctx); + break; + } + } +} + +int litehtml::flex_item::get_last_baseline(baseline::_baseline_type type) const +{ + if(type == baseline::baseline_type_top) + { + return el->get_last_baseline(); + } else if(type == baseline::baseline_type_bottom) + { + return el->height() - el->get_last_baseline(); + } + return 0; +} + +int litehtml::flex_item::get_first_baseline(litehtml::baseline::_baseline_type type) const +{ + if(type == baseline::baseline_type_top) + { + return el->get_first_baseline(); + } else if(type == baseline::baseline_type_bottom) + { + return el->height() - el->get_first_baseline(); + } + return 0; +} + + +//////////////////////////////////////////////////////////////////////////////////// + +void litehtml::flex_item_row_direction::direction_specific_init(const litehtml::containing_block_context &self_size, + litehtml::formatting_context *fmt_ctx) +{ + if(el->css().get_margins().left.is_predefined()) + { + auto_margin_main_start = 0; + } + if(el->css().get_margins().right.is_predefined()) + { + auto_margin_main_end = 0; + } + if(el->css().get_margins().top.is_predefined()) + { + auto_margin_cross_start = true; + } + if(el->css().get_margins().bottom.is_predefined()) + { + auto_margin_cross_end = true; + } + def_value<int> content_size(0); + if (el->css().get_min_width().is_predefined()) + { + min_size = el->render(0, 0, + self_size.new_width(el->content_offset_width(), + containing_block_context::size_mode_content), fmt_ctx); + content_size = min_size; + } else + { + min_size = el->css().get_min_width().calc_percent(self_size.render_width) + + el->content_offset_width(); + } + if (!el->css().get_max_width().is_predefined()) + { + max_size = el->css().get_max_width().calc_percent(self_size.render_width) + + el->content_offset_width(); + } + bool flex_basis_predefined = el->css().get_flex_basis().is_predefined(); + int predef = flex_basis_auto; + if(flex_basis_predefined) + { + predef = el->css().get_flex_basis().predef(); + } else + { + if(el->css().get_flex_basis().val() < 0) + { + flex_basis_predefined = true; + } + } + + if (flex_basis_predefined) + { + switch (predef) + { + case flex_basis_auto: + if (!el->css().get_width().is_predefined()) + { + base_size = el->css().get_width().calc_percent(self_size.render_width) + + el->content_offset_width(); + break; + } + // if width is not predefined, use content size as base size + case flex_basis_fit_content: + case flex_basis_content: + base_size = el->render(0, 0, self_size.new_width(self_size.render_width + el->content_offset_width(), + containing_block_context::size_mode_content | + containing_block_context::size_mode_exact_width), + fmt_ctx); + break; + case flex_basis_min_content: + if(content_size.is_default()) + { + content_size = el->render(0, 0, + self_size.new_width(el->content_offset_width(), + containing_block_context::size_mode_content), + fmt_ctx); + } + base_size = content_size; + break; + case flex_basis_max_content: + el->render(0, 0, self_size, fmt_ctx); + base_size = el->width(); + break; + default: + base_size = 0; + break; + } + } else + { + base_size = el->css().get_flex_basis().calc_percent(self_size.render_width) + + el->content_offset_width(); + base_size = std::max(base_size, min_size); + } +} + +void litehtml::flex_item_row_direction::apply_main_auto_margins() +{ + // apply auto margins to item + if(!auto_margin_main_start.is_default()) + { + el->get_margins().left = auto_margin_main_start; + el->pos().x += auto_margin_main_start; + } + if(!auto_margin_main_end.is_default()) el->get_margins().right = auto_margin_main_end; +} + +bool litehtml::flex_item_row_direction::apply_cross_auto_margins(int cross_size) +{ + if(auto_margin_cross_end || auto_margin_cross_start) + { + int margins_num = 0; + if(auto_margin_cross_end) + { + margins_num++; + } + if(auto_margin_cross_start) + { + margins_num++; + } + int margin = (cross_size - el->height()) / margins_num; + if(auto_margin_cross_start) + { + el->get_margins().top = margin; + el->pos().y = el->content_offset_top(); + } + if(auto_margin_cross_end) + { + el->get_margins().bottom = margin; + } + return true; + } + return false; +} + +void litehtml::flex_item_row_direction::set_main_position(int pos) +{ + el->pos().x = pos + el->content_offset_left(); +} + +void litehtml::flex_item_row_direction::set_cross_position(int pos) +{ + el->pos().y = pos + el->content_offset_top(); +} + +void litehtml::flex_item_row_direction::align_stretch(flex_line &ln, const containing_block_context &self_size, + formatting_context *fmt_ctx) +{ + set_cross_position(ln.cross_start); + if (el->css().get_height().is_predefined()) + { + el->render(el->left(), el->top(), self_size.new_width_height( + el->pos().width + el->box_sizing_width(), + ln.cross_size - el->content_offset_height() + el->box_sizing_height(), + containing_block_context::size_mode_exact_width | + containing_block_context::size_mode_exact_height + ), fmt_ctx); + apply_main_auto_margins(); + } +} + +void litehtml::flex_item_row_direction::align_baseline(litehtml::flex_line &ln, + const containing_block_context &self_size, + formatting_context *fmt_ctx) +{ + if (align & flex_align_items_last) + { + set_cross_position(ln.cross_start + ln.last_baseline.get_offset_from_top(ln.cross_size) - el->get_last_baseline()); + } else + { + set_cross_position(ln.cross_start + ln.first_baseline.get_offset_from_top(ln.cross_size) - el->get_first_baseline()); + } +} + +int litehtml::flex_item_row_direction::get_el_main_size() +{ + return el->width(); +} + +int litehtml::flex_item_row_direction::get_el_cross_size() +{ + return el->height(); +} + +//////////////////////////////////////////////////////////////////////////////////// + +void litehtml::flex_item_column_direction::direction_specific_init(const litehtml::containing_block_context &self_size, + litehtml::formatting_context *fmt_ctx) +{ + if(el->css().get_margins().top.is_predefined()) + { + auto_margin_main_start = 0; + } + if(el->css().get_margins().bottom.is_predefined()) + { + auto_margin_main_end = 0; + } + if(el->css().get_margins().left.is_predefined()) + { + auto_margin_cross_start = true; + } + if(el->css().get_margins().right.is_predefined()) + { + auto_margin_cross_end = true; + } + if (el->css().get_min_height().is_predefined()) + { + el->render(0, 0, self_size.new_width(self_size.render_width, containing_block_context::size_mode_content), fmt_ctx); + min_size = el->height(); + } else + { + min_size = el->css().get_min_height().calc_percent(self_size.height) + + el->content_offset_height(); + } + if (!el->css().get_max_height().is_predefined()) + { + max_size = el->css().get_max_height().calc_percent(self_size.height) + + el->content_offset_width(); + } + + bool flex_basis_predefined = el->css().get_flex_basis().is_predefined(); + int predef = flex_basis_auto; + if(flex_basis_predefined) + { + predef = el->css().get_flex_basis().predef(); + } else + { + if(el->css().get_flex_basis().val() < 0) + { + flex_basis_predefined = true; + } + } + + if (flex_basis_predefined) + { + switch (predef) + { + case flex_basis_auto: + if (!el->css().get_height().is_predefined()) + { + base_size = el->css().get_height().calc_percent(self_size.height) + + el->content_offset_height(); + break; + } + case flex_basis_max_content: + case flex_basis_fit_content: + el->render(0, 0, self_size, fmt_ctx); + base_size = el->height(); + break; + case flex_basis_min_content: + base_size = min_size; + break; + default: + base_size = 0; + } + } else + { + if(el->css().get_flex_basis().units() == css_units_percentage) + { + if(self_size.height.type == containing_block_context::cbc_value_type_absolute) + { + base_size = el->css().get_flex_basis().calc_percent(self_size.height) + + el->content_offset_height(); + } else + { + base_size = 0; + } + } else + { + base_size = (int) el->css().get_flex_basis().val() + el->content_offset_height(); + } + base_size = std::max(base_size, min_size); + } +} + +void litehtml::flex_item_column_direction::apply_main_auto_margins() +{ + // apply auto margins to item + if(!auto_margin_main_start.is_default()) + { + el->get_margins().top = auto_margin_main_start; + el->pos().y += auto_margin_main_start; + } + if(!auto_margin_main_end.is_default()) el->get_margins().bottom = auto_margin_main_end; +} + +bool litehtml::flex_item_column_direction::apply_cross_auto_margins(int cross_size) +{ + if(auto_margin_cross_end || auto_margin_cross_start) + { + int margins_num = 0; + if(auto_margin_cross_end) + { + margins_num++; + } + if(auto_margin_cross_start) + { + margins_num++; + } + int margin = (cross_size - el->width()) / margins_num; + if(auto_margin_cross_start) + { + el->get_margins().left = margin; + el->pos().x += el->content_offset_left(); + } + if(auto_margin_cross_end) + { + el->get_margins().right = margin; + } + } + return false; +} + +void litehtml::flex_item_column_direction::set_main_position(int pos) +{ + el->pos().y = pos + el->content_offset_top(); +} + +void litehtml::flex_item_column_direction::set_cross_position(int pos) +{ + el->pos().x = pos + el->content_offset_left(); +} + +void litehtml::flex_item_column_direction::align_stretch(flex_line &ln, const containing_block_context &self_size, + formatting_context *fmt_ctx) +{ + /// MAIN: Y + /// CROSS: X + if (!el->css().get_width().is_predefined()) + { + el->render(ln.cross_start, + el->pos().y - el->content_offset_top(), + self_size.new_width_height(ln.cross_size - el->content_offset_width() + el->box_sizing_width(), + main_size - el->content_offset_height() + el->box_sizing_height(), + containing_block_context::size_mode_exact_height), + fmt_ctx, false); + } else + { + el->render(ln.cross_start, + el->pos().y - el->content_offset_top(), + self_size.new_width_height( + ln.cross_size - el->content_offset_width() + el->box_sizing_width(), + main_size - el->content_offset_height() + el->box_sizing_height(), + containing_block_context::size_mode_exact_width | + containing_block_context::size_mode_exact_height), + fmt_ctx, false); + } + /// Apply auto margins after rendering + apply_main_auto_margins(); +} + +void litehtml::flex_item_column_direction::align_baseline(litehtml::flex_line &ln, + const containing_block_context &self_size, + formatting_context *fmt_ctx) +{ + // The fallback alignment for first baseline is start, the one for last baseline is end. + if(align & flex_align_items_last) + { + if(ln.reverse_cross) + { + set_cross_position(ln.cross_start); + } else + { + set_cross_position(ln.cross_start + ln.cross_size - get_el_cross_size()); + } + } else + { + if(!ln.reverse_cross) + { + set_cross_position(ln.cross_start); + } else + { + set_cross_position(ln.cross_start + ln.cross_size - get_el_cross_size()); + } + } +} + +int litehtml::flex_item_column_direction::get_el_main_size() +{ + return el->height(); +} + +int litehtml::flex_item_column_direction::get_el_cross_size() +{ + return el->width(); +} diff --git a/src/plugins/litehtml_viewer/litehtml/flex_item.h b/src/plugins/litehtml_viewer/litehtml/flex_item.h @@ -0,0 +1,137 @@ +#ifndef LITEHTML_FLEX_ITEM_H +#define LITEHTML_FLEX_ITEM_H + +#include <functional> +#include "formatting_context.h" + +namespace litehtml +{ + class flex_line; + + /** + * Base class for flex item + */ + class flex_item + { + public: + std::shared_ptr<render_item> el; + int base_size; + int min_size; + def_value<int> max_size; + int main_size; + int grow; + int shrink; + int scaled_flex_shrink_factor; + bool frozen; + int order; + int src_order; + def_value<int> auto_margin_main_start; + def_value<int> auto_margin_main_end; + bool auto_margin_cross_start; + bool auto_margin_cross_end; + flex_align_items align; + + explicit flex_item(std::shared_ptr<render_item> &_el) : + el(_el), + align(flex_align_items_auto), + grow(0), + base_size(0), + shrink(0), + min_size(0), + frozen(false), + main_size(0), + max_size(0), + order(0), + src_order(0), + scaled_flex_shrink_factor(0), + auto_margin_main_start(0), + auto_margin_main_end(0), + auto_margin_cross_start(false), + auto_margin_cross_end(false) + {} + + virtual ~flex_item() = default; + + bool operator<(const flex_item& b) const + { + if(order < b.order) return true; + if(order == b.order) return src_order < b.src_order; + return false; + } + void init(const litehtml::containing_block_context &self_size, + litehtml::formatting_context *fmt_ctx, flex_align_items align_items); + virtual void apply_main_auto_margins() = 0; + virtual bool apply_cross_auto_margins(int cross_size) = 0; + virtual void set_main_position(int pos) = 0; + virtual void set_cross_position(int pos) = 0; + virtual int get_el_main_size() = 0; + virtual int get_el_cross_size() = 0; + + void place(flex_line &ln, int main_pos, + const containing_block_context &self_size, + formatting_context *fmt_ctx); + int get_last_baseline(baseline::_baseline_type type) const; + int get_first_baseline(baseline::_baseline_type type) const; + + protected: + virtual void direction_specific_init(const litehtml::containing_block_context &self_size, + litehtml::formatting_context *fmt_ctx) = 0; + virtual void align_stretch(flex_line &ln, const containing_block_context &self_size, + formatting_context *fmt_ctx) = 0; + virtual void align_baseline(flex_line &ln, + const containing_block_context &self_size, + formatting_context *fmt_ctx) = 0; + }; + + /** + * Flex item with "flex-direction: row" or " flex-direction: row-reverse" + */ + class flex_item_row_direction : public flex_item + { + public: + explicit flex_item_row_direction(std::shared_ptr<render_item> &_el) : flex_item(_el) {} + + void apply_main_auto_margins() override; + bool apply_cross_auto_margins(int cross_size) override; + void set_main_position(int pos) override; + void set_cross_position(int pos) override; + int get_el_main_size() override; + int get_el_cross_size() override; + + protected: + void direction_specific_init(const litehtml::containing_block_context &self_size, + litehtml::formatting_context *fmt_ctx) override; + void align_stretch(flex_line &ln, const containing_block_context &self_size, + formatting_context *fmt_ctx) override; + void align_baseline(flex_line &ln, + const containing_block_context &self_size, + formatting_context *fmt_ctx) override; + }; + + /** + * Flex item with "flex-direction: column" or " flex-direction: column-reverse" + */ + class flex_item_column_direction : public flex_item + { + public: + explicit flex_item_column_direction(std::shared_ptr<render_item> &_el) : flex_item(_el) {} + + void apply_main_auto_margins() override; + bool apply_cross_auto_margins(int cross_size) override; + void set_main_position(int pos) override; + void set_cross_position(int pos) override; + int get_el_main_size() override; + int get_el_cross_size() override; + + protected: + void direction_specific_init(const litehtml::containing_block_context &self_size, + litehtml::formatting_context *fmt_ctx) override; + void align_stretch(flex_line &ln, const containing_block_context &self_size, + formatting_context *fmt_ctx) override; + void align_baseline(flex_line &ln, + const containing_block_context &self_size, + formatting_context *fmt_ctx) override; + }; +} + +#endif //LITEHTML_FLEX_ITEM_H diff --git a/src/plugins/litehtml_viewer/litehtml/flex_line.cpp b/src/plugins/litehtml_viewer/litehtml/flex_line.cpp @@ -0,0 +1,454 @@ +#include "html.h" +#include "flex_line.h" +#include "flex_item.h" +#include "render_item.h" + +void litehtml::flex_line::distribute_free_space(int container_main_size) +{ + // Determine the used flex factor. Sum the outer hypothetical main sizes of all items on the line. + // If the sum is less than the flex container’s inner main size, use the flex grow factor for the + // rest of this algorithm; otherwise, use the flex shrink factor. + int initial_free_space = container_main_size - base_size; + bool grow; + int total_flex_factor; + if(initial_free_space < 0) + { + grow = false; + total_flex_factor = total_shrink; + // Flex values between 0 and 1 have a somewhat special behavior: when the sum of the flex values on the line + // is less than 1, they will take up less than 100% of the free space. + // https://www.w3.org/TR/css-flexbox-1/#valdef-flex-flex-grow + if(total_flex_factor < 1000) + { + for(auto &item : items) + { + item->main_size += initial_free_space * item->shrink / 1000; + } + return; + } + } else + { + grow = true; + total_flex_factor = total_grow; + // Flex values between 0 and 1 have a somewhat special behavior: when the sum of the flex values on the line + // is less than 1, they will take up less than 100% of the free space. + // https://www.w3.org/TR/css-flexbox-1/#valdef-flex-flex-grow + if(total_flex_factor < 1000) + { + for(auto &item : items) + { + item->main_size += initial_free_space * item->grow / 1000; + } + return; + } + } + + if(total_flex_factor > 0) + { + bool processed = true; + while (processed) + { + int sum_scaled_flex_shrink_factor = 0; + int sum_flex_factors = 0; + int remaining_free_space = container_main_size; + int total_not_frozen = 0; + for (auto &item: items) + { + if (!item->frozen) + { + sum_scaled_flex_shrink_factor += item->scaled_flex_shrink_factor; + if(grow) + { + sum_flex_factors += item->grow; + } else + { + sum_flex_factors += item->shrink; + } + remaining_free_space -= item->base_size; + total_not_frozen++; + } else + { + remaining_free_space -= item->main_size; + } + } + // Check for flexible items. If all the flex items on the line are frozen, free space has + // been distributed; exit this loop. + if (!total_not_frozen) break; + + remaining_free_space = abs(remaining_free_space); + // c. Distribute free space proportional to the flex factors. + // If the remaining free space is zero + // Do nothing. + if (!remaining_free_space) + { + processed = false; + } else + { + int total_clamped = 0; + for (auto &item: items) + { + if (!item->frozen) + { + if(!grow) + { + // If using the flex shrink factor + // For every unfrozen item on the line, multiply its flex shrink factor by its + // inner flex base size, and note this as its scaled flex shrink factor. Find + // the ratio of the item’s scaled flex shrink factor to the sum of the scaled + // flex shrink factors of all unfrozen items on the line. Set the item’s target + // main size to its flex base size minus a fraction of the absolute value of the + // remaining free space proportional to the ratio. + int scaled_flex_shrink_factor = item->base_size * item->shrink; + item->main_size = (int) ((float) item->base_size - (float) remaining_free_space * + (float) scaled_flex_shrink_factor / + (float) sum_scaled_flex_shrink_factor); + + // d. Fix min/max violations. Clamp each non-frozen item’s target main size by its used + // min and max main sizes and floor its content-box size at zero. If the item’s target + // main size was made smaller by this, it’s a max violation. If the item’s target main + // size was made larger by this, it’s a min violation. + if (item->main_size <= item->min_size) + { + total_clamped++; + item->main_size = item->min_size; + item->frozen = true; + } + if(!item->max_size.is_default() && item->main_size >= item->max_size) + { + total_clamped++; + item->main_size = item->max_size; + item->frozen = true; + } + } else + { + // If using the flex grow factor + // Find the ratio of the item’s flex grow factor to the sum of the flex grow + // factors of all unfrozen items on the line. Set the item’s target main size to + // its flex base size plus a fraction of the remaining free space proportional + // to the ratio. + item->main_size = (int) ((float) item->base_size + + (float) remaining_free_space * (float) item->grow / + (float) total_flex_factor); + // d. Fix min/max violations. Clamp each non-frozen item’s target main size by its used + // min and max main sizes and floor its content-box size at zero. If the item’s target + // main size was made smaller by this, it’s a max violation. If the item’s target main + // size was made larger by this, it’s a min violation. + if (item->main_size >= container_main_size) + { + total_clamped++; + item->main_size = container_main_size; + item->frozen = true; + } + if(!item->max_size.is_default() && item->main_size >= item->max_size) + { + total_clamped++; + item->main_size = item->max_size; + item->frozen = true; + } + } + } + } + if (total_clamped == 0) processed = false; + } + } + // Distribute remaining after algorithm space + int sum_main_size = 0; + for(auto &item : items) + { + sum_main_size += item->main_size; + } + int free_space = container_main_size - sum_main_size; + if(free_space > 0) + { + for(auto &item : items) + { + if(free_space == 0) break; + item->main_size++; + free_space--; + } + } + } +} + +bool litehtml::flex_line::distribute_main_auto_margins(int free_main_size) +{ + if(free_main_size > 0 && (num_auto_margin_main_start || num_auto_margin_main_end)) + { + int add = (int) (free_main_size / (items.size() * 2)); + for (auto &item: items) + { + if(!item->auto_margin_main_start.is_default()) + { + item->auto_margin_main_start = add; + item->main_size += add; + main_size += add; + free_main_size -= add; + } + if(!item->auto_margin_main_end.is_default()) + { + item->auto_margin_main_end = add; + item->main_size += add; + main_size += add; + free_main_size -= add; + } + } + while (free_main_size > 0) + { + for (auto &item: items) + { + if(!item->auto_margin_main_start.is_default()) + { + item->auto_margin_main_start = item->auto_margin_main_start + 1; + free_main_size--; + if(!free_main_size) break; + } + if(!item->auto_margin_main_end.is_default()) + { + item->auto_margin_main_end = item->auto_margin_main_end + 1; + free_main_size--; + if(!free_main_size) break; + } + } + } + return true; + } + return false; +} + +void litehtml::flex_line::init(int container_main_size, bool fit_container, bool is_row_direction, + const litehtml::containing_block_context &self_size, + litehtml::formatting_context *fmt_ctx) +{ + cross_size = 0; + main_size = 0; + first_baseline.set(0, baseline::baseline_type_none); + last_baseline.set(0, baseline::baseline_type_none); + + if(!fit_container) + { + distribute_free_space(container_main_size); + } + + if(is_row_direction) + { + def_value<int> first_baseline_top = 0; + def_value<int> first_baseline_bottom = 0; + def_value<int> last_baseline_top = 0; + def_value<int> last_baseline_bottom = 0; + int non_baseline_height = 0; + + // Calculate maximum cross size + def_value<int> max_cross_size(0); + if(self_size.height.type != containing_block_context::cbc_value_type_auto) + { + max_cross_size = self_size.height; + } + if(self_size.max_height.type != containing_block_context::cbc_value_type_none) + { + if(max_cross_size.is_default()) + { + max_cross_size = self_size.max_height; + } else + { + max_cross_size = std::max((int) max_cross_size, (int) self_size.max_height); + } + } + + /// Render items into new size + /// Find line cross_size + /// Find line first/last baseline + for (auto &item: items) + { + item->el->render(0, + 0, + self_size.new_width(item->main_size - item->el->content_offset_width(), containing_block_context::size_mode_exact_width), fmt_ctx, false); + + if((item->align & 0xFF) == flex_align_items_baseline) + { + if(item->align & flex_align_items_last) + { + last_baseline.type(reverse_cross ? baseline::baseline_type_top : baseline::baseline_type_bottom); + + int top = -item->el->get_last_baseline(); + int bottom = top + item->el->height(); + + if(last_baseline_top.is_default()) last_baseline_top = top; + else last_baseline_top = std::min((int) last_baseline_top, top); + + if(last_baseline_bottom.is_default()) last_baseline_bottom = bottom; + else last_baseline_bottom = std::max((int)last_baseline_bottom, bottom); + } else + { + first_baseline.type(reverse_cross ? baseline::baseline_type_bottom : baseline::baseline_type_top); + int top = -item->el->get_first_baseline(); + int bottom = top + item->el->height(); + + if(first_baseline_top.is_default()) first_baseline_top = top; + else first_baseline_top = std::min((int) first_baseline_top, top); + + if(first_baseline_bottom.is_default()) first_baseline_bottom = bottom; + else first_baseline_bottom = std::max((int) first_baseline_bottom, bottom); + } + } else + { + non_baseline_height = std::max(non_baseline_height, item->el->height()); + } + main_size += item->el->width(); + } + + cross_size = std::max(first_baseline_bottom - first_baseline_top,last_baseline_bottom - last_baseline_top); + cross_size = std::max(cross_size, non_baseline_height); + if(!max_cross_size.is_default() && cross_size > max_cross_size) + { + cross_size = max_cross_size; + } + + first_baseline.calc(first_baseline_top, first_baseline_bottom); + last_baseline.calc(last_baseline_top, last_baseline_bottom); + } else + { + // Calculate maximum cross size + def_value<int> max_cross_size(0); + if(self_size.width.type != containing_block_context::cbc_value_type_auto) + { + max_cross_size = self_size.width; + } + if(self_size.max_width.type != containing_block_context::cbc_value_type_none) + { + if(max_cross_size.is_default()) + { + max_cross_size = self_size.max_width; + } else + { + max_cross_size = std::max((int) max_cross_size, (int) self_size.max_width); + } + } + + for (auto &item: items) + { + int el_ret_width = item->el->render(0, + 0, + self_size, fmt_ctx, false); + item->el->render(0, + 0, + self_size.new_width_height(el_ret_width - item->el->content_offset_width(), + item->main_size - item->el->content_offset_height(), + containing_block_context::size_mode_exact_width | + containing_block_context::size_mode_exact_height), + fmt_ctx, false); + main_size += item->el->height(); + cross_size = std::max(cross_size, item->el->width()); + } + if(!max_cross_size.is_default() && cross_size > max_cross_size) + { + cross_size = max_cross_size; + } + } +} + +int litehtml::flex_line::calculate_items_position(int container_main_size, + flex_justify_content justify_content, + bool is_row_direction, + const containing_block_context &self_size, + formatting_context *fmt_ctx) +{ + /// Distribute main axis free space for auto-margins + int free_main_size = container_main_size - main_size; + distribute_main_auto_margins(free_main_size); + free_main_size = container_main_size - main_size; + + /// Fix justify-content property + switch (justify_content) + { + case flex_justify_content_left: + case flex_justify_content_right: + if(!is_row_direction) + { + justify_content = flex_justify_content_start; + } + break; + case flex_justify_content_space_between: + // If the leftover free-space is negative or there is only a single flex item on the line, this + // value is identical to flex-start. + if(items.size() == 1 || free_main_size < 0) justify_content = flex_justify_content_flex_start; + break; + case flex_justify_content_space_around: + case flex_justify_content_space_evenly: + // If the leftover free-space is negative or there is only a single flex item on the line, this + // value is identical to center + if(items.size() == 1 || free_main_size < 0) justify_content = flex_justify_content_center; + break; + default: + break; + } + + /// Distribute free main size using justify-content property + int main_pos = 0; + int add_before_item = 0; + int add_after_item = 0; + int item_remainder = 0; + + /// find initial main position and spaces between items + switch (justify_content) + { + + case flex_justify_content_right: + main_pos = free_main_size; + break; + case flex_justify_content_left: + case flex_justify_content_start: + main_pos = 0; + break; + case flex_justify_content_end: + main_pos = free_main_size; + break; + case flex_justify_content_flex_end: + if(!reverse_main) + { + main_pos = free_main_size; + } + break; + case flex_justify_content_center: + main_pos = free_main_size / 2; + break; + case flex_justify_content_space_between: + add_after_item = free_main_size / ((int) items.size() - 1); + item_remainder = free_main_size - (add_after_item * ((int) items.size() - 1)); + break; + case flex_justify_content_space_around: + add_after_item = add_before_item = free_main_size / ((int) items.size() * 2); + item_remainder = free_main_size - (add_after_item * (int) items.size() * 2); + break; + case flex_justify_content_space_evenly: + add_before_item = free_main_size / ((int) items.size() + 1); + item_remainder = free_main_size - add_before_item * ((int) items.size() + 1); + break; + default: + if(reverse_main) + { + main_pos = free_main_size; + } + break; + } + + /// Place all items in main and cross positions + int height = 0; + for(auto &item : items) + { + main_pos += add_before_item; + if(add_before_item > 0 && item_remainder > 0) + { + main_pos++; + item_remainder--; + } + item->place(*this, main_pos, self_size, fmt_ctx); + main_pos += item->get_el_main_size() + add_after_item; + if(add_after_item > 0 && item_remainder > 0) + { + main_pos++; + item_remainder--; + } + height = std::max(height, item->el->bottom()); + } + return height; +} diff --git a/src/plugins/litehtml_viewer/litehtml/flex_line.h b/src/plugins/litehtml_viewer/litehtml/flex_line.h @@ -0,0 +1,56 @@ +#ifndef LITEHTML_FLEX_LINE_H +#define LITEHTML_FLEX_LINE_H + +#include "formatting_context.h" + +namespace litehtml +{ + class flex_item; + + class flex_line + { + public: + std::list<std::shared_ptr<flex_item>> items; + int cross_start; // for row direction: top. for column direction: left + int main_size; // sum of all items main size + int cross_size; // sum of all items cross size + int base_size; + int total_grow; + int total_shrink; + int num_auto_margin_main_start; // number of items with auto margin left/top + int num_auto_margin_main_end; // number of items with auto margin right/bottom + baseline first_baseline; + baseline last_baseline; + bool reverse_main; + bool reverse_cross; + + flex_line(bool _reverse_main, bool _reverse_cross) : + cross_size(0), + cross_start(0), + total_grow(0), + base_size(0), + total_shrink(0), + main_size(0), + num_auto_margin_main_start(0), + num_auto_margin_main_end(0), + first_baseline(), + last_baseline(), + reverse_main(_reverse_main), + reverse_cross(_reverse_cross) + {} + + void init(int container_main_size, bool fit_container, bool is_row_direction, + const litehtml::containing_block_context &self_size, + litehtml::formatting_context *fmt_ctx); + bool distribute_main_auto_margins(int free_main_size); + int calculate_items_position(int container_main_size, + flex_justify_content justify_content, + bool is_row_direction, + const containing_block_context &self_size, + formatting_context *fmt_ctx); + protected: + void distribute_free_space(int container_main_size); + }; +} + +#endif //LITEHTML_FLEX_LINE_H diff --git a/src/plugins/litehtml_viewer/litehtml/html.cpp b/src/plugins/litehtml_viewer/litehtml/html.cpp @@ -2,9 +2,9 @@ #include "types.h" #include "utf8_strings.h" -void litehtml::trim(string &s) +void litehtml::trim(string &s, const string& chars_to_trim) { - string::size_type pos = s.find_first_not_of(" \n\r\t"); + string::size_type pos = s.find_first_not_of(chars_to_trim); if(pos != string::npos) { s.erase(s.begin(), s.begin() + pos); @@ -14,7 +14,7 @@ void litehtml::trim(string &s) s = ""; return; } - pos = s.find_last_not_of(" \n\r\t"); + pos = s.find_last_not_of(chars_to_trim); if(pos != string::npos) { s.erase(s.begin() + pos + 1, s.end()); @@ -277,3 +277,14 @@ litehtml::string litehtml::get_escaped_string(const string& in_str) } return ret; } + +bool litehtml::is_number(const string& string, const bool allow_dot) { + for (auto ch : string) + { + if (!(t_isdigit(ch) || (allow_dot && ch == '.'))) + { + return false; + } + } + return true; +} diff --git a/src/plugins/litehtml_viewer/litehtml/html.h b/src/plugins/litehtml_viewer/litehtml/html.h @@ -23,7 +23,7 @@ namespace litehtml { - void trim(string &s); + void trim(string &s, const string& chars_to_trim = " \n\r\t"); void lcase(string &s); int value_index(const string& val, const string& strings, int defValue = -1, char delim = ';'); string index_value(int index, const string& strings, char delim = ';'); @@ -36,7 +36,9 @@ namespace litehtml int t_strcasecmp(const char *s1, const char *s2); int t_strncasecmp(const char *s1, const char *s2, size_t n); - + + bool is_number(const string& string, const bool allow_dot = 1); + inline int t_isdigit(int c) { return (c >= '0' && c <= '9'); diff --git a/src/plugins/litehtml_viewer/litehtml/html_tag.cpp b/src/plugins/litehtml_viewer/litehtml/html_tag.cpp @@ -367,6 +367,11 @@ int litehtml::html_tag::get_enum_property(string_id name, bool inherited, int de return get_property_impl<int, prop_type_enum_item, &property_value::m_enum_item>(name, inherited, default_value, css_properties_member_offset); } +int litehtml::html_tag::get_int_property(string_id name, bool inherited, int default_value, uint_ptr css_properties_member_offset) const +{ + return get_property_impl<int, prop_type_enum_item, &property_value::m_enum_item>(name, inherited, default_value, css_properties_member_offset); +} + litehtml::css_length litehtml::html_tag::get_length_property(string_id name, bool inherited, css_length default_value, uint_ptr css_properties_member_offset) const { return get_property_impl<css_length, prop_type_length, &property_value::m_length>(name, inherited, default_value, css_properties_member_offset); @@ -1548,9 +1553,33 @@ litehtml::element::ptr litehtml::html_tag::get_element_after(const style& style, return nullptr; } + +void litehtml::html_tag::handle_counter_properties() +{ + const auto& reset_property = m_style.get_property(string_id::_counter_reset_); + if (reset_property.m_type == prop_type_string_vector) { + auto reset_function = [&](const string_id&name_id, const int value) { + reset_counter(name_id, value); + }; + parse_counter_tokens(reset_property.m_string_vector, 0, reset_function); + return; + } + + const auto& inc_property = m_style.get_property(string_id::_counter_increment_); + if (inc_property.m_type == prop_type_string_vector) { + auto inc_function = [&](const string_id&name_id, const int value) { + increment_counter(name_id, value); + }; + parse_counter_tokens(inc_property.m_string_vector, 1, inc_function); + return; + } +} + + void litehtml::html_tag::add_style(const style& style) { m_style.combine(style); + handle_counter_properties(); } void litehtml::html_tag::refresh_styles() diff --git a/src/plugins/litehtml_viewer/litehtml/html_tag.h b/src/plugins/litehtml_viewer/litehtml/html_tag.h @@ -72,6 +72,7 @@ namespace litehtml template<class Type, property_type property_value_type, Type property_value::* property_value_member> const Type& get_property_impl (string_id name, bool inherited, const Type& default_value, uint_ptr css_properties_member_offset) const; int get_enum_property (string_id name, bool inherited, int default_value, uint_ptr css_properties_member_offset) const override; + int get_int_property (string_id name, bool inherited, int default_value, uint_ptr css_properties_member_offset) const override; css_length get_length_property(string_id name, bool inherited, css_length default_value, uint_ptr css_properties_member_offset) const override; web_color get_color_property (string_id name, bool inherited, web_color default_value, uint_ptr css_properties_member_offset) const override; string get_string_property(string_id name, bool inherited, const string& default_value, uint_ptr css_properties_member_offset) const override; @@ -119,6 +120,10 @@ namespace litehtml string get_list_marker_text(int index); element::ptr get_element_before(const style& style, bool create); element::ptr get_element_after(const style& style, bool create); + + private: + void handle_counter_properties(); + }; /************************************************************************/ diff --git a/src/plugins/litehtml_viewer/litehtml/line_box.cpp b/src/plugins/litehtml_viewer/litehtml/line_box.cpp @@ -363,7 +363,7 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::finish case va_super: { int bl = calc_va_baseline(current_context, lbi->get_el()->css().get_vertical_align(), current_context.fm, line_top, line_bottom); - lbi->pos().y = bl - lbi->get_el()->height() + lbi->get_el()->get_base_line() + + lbi->pos().y = bl - lbi->get_el()->get_last_baseline() + lbi->get_el()->content_offset_top(); } break; @@ -374,7 +374,7 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::finish lbi->pos().y = line_top + lbi->get_el()->content_offset_top(); break; case va_baseline: - lbi->pos().y = current_context.baseline - lbi->get_el()->height() + lbi->get_el()->get_base_line() + + lbi->pos().y = current_context.baseline - lbi->get_el()->get_last_baseline() + lbi->get_el()->content_offset_top(); break; case va_text_top: @@ -555,10 +555,23 @@ bool litehtml::line_box::can_hold(const std::unique_ptr<line_box_item>& item, wh if(item->get_type() == line_box_item::type_text_part) { + // force new line on floats clearing + if (item->get_el()->src_el()->is_break() && item->get_el()->src_el()->css().get_clear() != clear_none) + { + return false; + } + auto last_el = get_last_text_part(); + // the first word is always can be hold + if(!last_el) + { + return true; + } + // force new line if the last placed element was line break - if (last_el && last_el->src_el()->is_break()) + // Skip If there are the only break item - this is float clearing + if (last_el && last_el->src_el()->is_break() && m_items.size() > 1) { return false; } @@ -597,6 +610,12 @@ bool litehtml::line_box::have_last_space() const bool litehtml::line_box::is_empty() const { if(m_items.empty()) return true; + if(m_items.size() == 1 && + m_items.front()->get_el()->src_el()->is_break() && + m_items.front()->get_el()->src_el()->css().get_clear() != clear_none) + { + return true; + } for (const auto& el : m_items) { if(el->get_type() == line_box_item::type_text_part) diff --git a/src/plugins/litehtml_viewer/litehtml/media_query.cpp b/src/plugins/litehtml_viewer/litehtml/media_query.cpp @@ -88,8 +88,7 @@ litehtml::media_query::ptr litehtml::media_query::create_from_string(const strin } } else { - query->m_media_type = (media_type) value_index(token, media_type_strings, media_type_all); - + query->m_media_type = (media_type) value_index(token, media_type_strings, media_type_none); } } diff --git a/src/plugins/litehtml_viewer/litehtml/render_block.cpp b/src/plugins/litehtml_viewer/litehtml/render_block.cpp @@ -208,24 +208,21 @@ int litehtml::render_item_block::_render(int x, int y, const containing_block_co bool requires_rerender = false; // when true, the second pass for content rendering is required // Set block width - if(self_size.width.type == containing_block_context::cbc_value_type_absolute) + if(!(containing_block_size.size_mode & containing_block_context::size_mode_content)) { - ret_width = m_pos.width = self_size.render_width; - } else if(self_size.width.type == containing_block_context::cbc_value_type_percentage) - { - m_pos.width = self_size.render_width; + if(self_size.width.type == containing_block_context::cbc_value_type_absolute) + { + ret_width = m_pos.width = self_size.render_width; + } else + { + m_pos.width = self_size.render_width; + } } else { - m_pos.width = self_size.render_width; - } - - // Fix width with min-width attribute - if(self_size.min_width.type != containing_block_context::cbc_value_type_none) - { - if(m_pos.width < self_size.min_width) + m_pos.width = ret_width; + if(self_size.width.type == containing_block_context::cbc_value_type_absolute && ret_width > self_size.width) { - m_pos.width = self_size.min_width; - requires_rerender = true; + ret_width = self_size.width; } } @@ -239,6 +236,19 @@ int litehtml::render_item_block::_render(int x, int y, const containing_block_co } } + // Fix width with min-width attribute + if(self_size.min_width.type != containing_block_context::cbc_value_type_none) + { + if(m_pos.width < self_size.min_width) + { + m_pos.width = self_size.min_width; + requires_rerender = true; + } + } else if(m_pos.width < 0) + { + m_pos.width = 0; + } + // re-render content with new width if required if (requires_rerender && !second_pass && !is_root()) { @@ -254,25 +264,44 @@ int litehtml::render_item_block::_render(int x, int y, const containing_block_co } // Set block height - if (self_size.height.type != containing_block_context::cbc_value_type_auto) + if (self_size.height.type != containing_block_context::cbc_value_type_auto && + !(containing_block_size.size_mode & containing_block_context::size_mode_content)) { - if(self_size.height > 0) + // TODO: Something wrong here + // Percentage height from undefined containing block height is usually <= 0 + if(self_size.height.type == containing_block_context::cbc_value_type_percentage) + { + if (self_size.height > 0) + { + m_pos.height = self_size.height; + } + } else { m_pos.height = self_size.height; } - if(src_el()->css().get_box_sizing() == box_sizing_border_box) + if (src_el()->css().get_box_sizing() == box_sizing_border_box) { m_pos.height -= box_sizing_height(); } } else if (src_el()->is_block_formatting_context()) - { + { // add the floats' height to the block height - int floats_height = fmt_ctx->get_floats_height(); - if (floats_height > m_pos.height) - { - m_pos.height = floats_height; - } - } + int floats_height = fmt_ctx->get_floats_height(); + if (floats_height > m_pos.height) + { + m_pos.height = floats_height; + } + } + if(containing_block_size.size_mode & containing_block_context::size_mode_content) + { + if(self_size.height.type == containing_block_context::cbc_value_type_absolute) + { + if(m_pos.height > self_size.height) + { + m_pos.height = self_size.height; + } + } + } // Fix height with min-height attribute if(self_size.min_height.type != containing_block_context::cbc_value_type_none) @@ -281,6 +310,9 @@ int litehtml::render_item_block::_render(int x, int y, const containing_block_co { m_pos.height = self_size.min_height; } + } else if(m_pos.height < 0) + { + m_pos.height = 0; } // Fix width with max-width attribute diff --git a/src/plugins/litehtml_viewer/litehtml/render_block_context.cpp b/src/plugins/litehtml_viewer/litehtml/render_block_context.cpp @@ -47,20 +47,26 @@ int litehtml::render_item_block_context::_render_content(int x, int y, bool seco // Collapse top margin if(is_first && collapse_top_margin()) { - child_top -= el->get_margins().top; - if(el->get_margins().top > get_margins().top) - { - m_margins.top = el->get_margins().top; - } + if(el->get_margins().top > 0) + { + child_top -= el->get_margins().top; + if (el->get_margins().top > get_margins().top) + { + m_margins.top = el->get_margins().top; + } + } } else { - if(last_margin > el->get_margins().top) - { - child_top -= el->get_margins().top; - } else - { - child_top -= last_margin; - } + if(el->get_margins().top > 0) + { + if (last_margin > el->get_margins().top) + { + child_top -= el->get_margins().top; + } else + { + child_top -= last_margin; + } + } } if(el->src_el()->is_replaced() || el->src_el()->is_block_formatting_context() || el->src_el()->css().get_display() == display_table) @@ -104,10 +110,9 @@ int litehtml::render_item_block_context::_render_content(int x, int y, bool seco } } - int block_height = 0; - if (get_predefined_height(block_height, self_size.height)) + if (self_size.height.type != containing_block_context::cbc_value_type_auto && self_size.height > 0) { - m_pos.height = block_height; + m_pos.height = self_size.height; } else { m_pos.height = child_top; @@ -127,3 +132,23 @@ int litehtml::render_item_block_context::_render_content(int x, int y, bool seco return ret_width; } + +int litehtml::render_item_block_context::get_first_baseline() +{ + if(m_children.empty()) + { + return height() - margin_bottom(); + } + const auto &item = m_children.front(); + return content_offset_top() + item->top() + item->get_first_baseline(); +} + +int litehtml::render_item_block_context::get_last_baseline() +{ + if(m_children.empty()) + { + return height() - margin_bottom(); + } + const auto &item = m_children.back(); + return content_offset_top() + item->top() + item->get_last_baseline(); +} diff --git a/src/plugins/litehtml_viewer/litehtml/render_block_context.h b/src/plugins/litehtml_viewer/litehtml/render_block_context.h @@ -23,6 +23,8 @@ namespace litehtml { return std::make_shared<render_item_block_context>(src_el()); } + int get_first_baseline() override; + int get_last_baseline() override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/render_flex.cpp b/src/plugins/litehtml_viewer/litehtml/render_flex.cpp @@ -4,12 +4,327 @@ int litehtml::render_item_flex::_render_content(int x, int y, bool second_pass, const containing_block_context &self_size, formatting_context* fmt_ctx) { - return 0; + bool is_row_direction = true; + bool reverse = false; + int container_main_size = self_size.render_width; + + switch (css().get_flex_direction()) + { + case flex_direction_column: + is_row_direction = false; + reverse = false; + break; + case flex_direction_column_reverse: + is_row_direction = false; + reverse = true; + break; + case flex_direction_row: + is_row_direction = true; + reverse = false; + break; + case flex_direction_row_reverse: + is_row_direction = true; + reverse = true; + break; + } + + bool single_line = css().get_flex_wrap() == flex_wrap_nowrap; + bool fit_container = false; + + if(!is_row_direction) + { + if(self_size.height.type != containing_block_context::cbc_value_type_auto) + { + container_main_size = self_size.height; + if (css().get_box_sizing() == box_sizing_border_box) + { + container_main_size -= box_sizing_height(); + } + } else + { + // Direction columns, height is auto - always in single line + container_main_size = 0; + single_line = true; + fit_container = true; + } + if(self_size.min_height.type != containing_block_context::cbc_value_type_auto && self_size.min_height > container_main_size) + { + container_main_size = self_size.min_height; + } + if(self_size.max_height.type != containing_block_context::cbc_value_type_auto && self_size.max_height > container_main_size) + { + container_main_size = self_size.max_height; + single_line = false; + } + } + + ///////////////////////////////////////////////////////////////// + /// Split flex items to lines + ///////////////////////////////////////////////////////////////// + m_lines = get_lines(self_size, fmt_ctx, is_row_direction, container_main_size, single_line); + + int el_y = 0; + int el_x = 0; + int sum_cross_size = 0; + int sum_main_size = 0; + int ret_width = 0; + + ///////////////////////////////////////////////////////////////// + /// Resolving Flexible Lengths + /// REF: https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths + ///////////////////////////////////////////////////////////////// + for(auto& ln : m_lines) + { + if(is_row_direction) + { + ret_width += ln.base_size; + } + ln.init(container_main_size, fit_container, is_row_direction, self_size, fmt_ctx); + sum_cross_size += ln.cross_size; + sum_main_size = std::max(sum_main_size, ln.main_size); + if(reverse) + { + ln.items.reverse(); + } + } + + int free_cross_size = 0; + int cross_start = 0; + bool is_wrap_reverse = css().get_flex_wrap() == flex_wrap_wrap_reverse; + if(container_main_size == 0) + { + container_main_size = sum_main_size; + } + + ///////////////////////////////////////////////////////////////// + /// Calculate free cross size + ///////////////////////////////////////////////////////////////// + if (is_row_direction) + { + cross_start = content_offset_top(); + if (self_size.height.type != containing_block_context::cbc_value_type_auto) + { + int height = self_size.height; + if (src_el()->css().get_box_sizing() == box_sizing_border_box) + { + height -= box_sizing_height(); + } + free_cross_size = height - sum_cross_size; + } + } else + { + cross_start = content_offset_left(); + free_cross_size = self_size.render_width - sum_cross_size; + ret_width = sum_cross_size; + } + + ///////////////////////////////////////////////////////////////// + /// Fix align-content property + ///////////////////////////////////////////////////////////////// + flex_align_content align_content = css().get_flex_align_content(); + if(align_content == flex_align_content_space_between) + { + // If the leftover free-space is negative or there is only a single flex line in the flex + // container, this value is identical to flex-start. + if (m_lines.size() == 1 || free_cross_size < 0) align_content = flex_align_content_flex_start; + } + if(align_content == flex_align_content_space_around) + { + // If the leftover free-space is negative or there is only a single flex line in the flex + // container, this value is identical to flex-start. + if (m_lines.size() == 1 || free_cross_size < 0) align_content = flex_align_content_center; + } + + ///////////////////////////////////////////////////////////////// + /// Distribute free cross size for align-content: stretch + ///////////////////////////////////////////////////////////////// + if(css().get_flex_align_content() == flex_align_content_stretch && free_cross_size > 0) + { + int add = (int)((double) free_cross_size / (double) m_lines.size()); + if(add > 0) + { + for (auto &ln: m_lines) + { + ln.cross_size += add; + free_cross_size -= add; + } + } + if(!m_lines.empty()) + { + while (free_cross_size > 0) + { + for (auto &ln: m_lines) + { + ln.cross_size++; + free_cross_size--; + } + } + } + } + + /// Reverse lines for flex-wrap: wrap-reverse + if(css().get_flex_wrap() == flex_wrap_wrap_reverse) + { + m_lines.reverse(); + } + + ///////////////////////////////////////////////////////////////// + /// Align flex lines + ///////////////////////////////////////////////////////////////// + int line_pos = 0; + int add_before_line = 0; + int add_after_line = 0; + switch (align_content) + { + case flex_align_content_flex_start: + if(is_wrap_reverse) + { + line_pos = free_cross_size; + } + break; + case flex_align_content_flex_end: + if(!is_wrap_reverse) + { + line_pos = free_cross_size; + } + break; + case flex_align_content_end: + line_pos = free_cross_size; + break; + case flex_align_content_center: + line_pos = free_cross_size / 2; + break; + case flex_align_content_space_between: + add_after_line = free_cross_size / ((int) m_lines.size() - 1); + break; + case flex_align_content_space_around: + add_before_line = add_after_line = free_cross_size / ((int) m_lines.size() * 2); + break; + default: + if(is_wrap_reverse) + { + line_pos = free_cross_size; + } + break; + } + for(auto &ln : m_lines) + { + line_pos += add_before_line; + ln.cross_start = line_pos; + line_pos += ln.cross_size + add_after_line; + } + + /// Fix justify-content property + flex_justify_content justify_content = css().get_flex_justify_content(); + if((justify_content == flex_justify_content_right || justify_content == flex_justify_content_left) && !is_row_direction) + { + justify_content = flex_justify_content_start; + } + + ///////////////////////////////////////////////////////////////// + /// Align flex items in flex lines + ///////////////////////////////////////////////////////////////// + int line_num = 0; + for(auto &ln : m_lines) + { + int height = ln.calculate_items_position(container_main_size, + justify_content, + is_row_direction, + self_size, + fmt_ctx); + line_num++; + m_pos.height = std::max(m_pos.height, height); + } + + // calculate the final position + m_pos.move_to(x, y); + m_pos.x += content_offset_left(); + m_pos.y += content_offset_top(); + + return ret_width; } -void litehtml::render_item_flex::draw_children(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex) +std::list<litehtml::flex_line> litehtml::render_item_flex::get_lines(const litehtml::containing_block_context &self_size, + litehtml::formatting_context *fmt_ctx, + bool is_row_direction, int container_main_size, + bool single_line) { + bool reverse_main; + bool reverse_cross = css().get_flex_wrap() == flex_wrap_wrap_reverse; + + if(is_row_direction) + { + reverse_main = css().get_flex_direction() == flex_direction_row_reverse; + } else + { + reverse_main = css().get_flex_direction() == flex_direction_column_reverse; + } + + std::list<flex_line> lines; + flex_line line(reverse_main, reverse_cross); + std::list<std::shared_ptr<flex_item>> items; + int src_order = 0; + bool sort_required = false; + def_value<int> prev_order(0); + + for( auto& el : m_children) + { + std::shared_ptr<flex_item> item = nullptr; + if(is_row_direction) + { + item = std::make_shared<flex_item_row_direction>(el); + } else + { + item = std::make_shared<flex_item_column_direction>(el); + } + item->init(self_size, fmt_ctx, css().get_flex_align_items()); + item->src_order = src_order++; + if(prev_order.is_default()) + { + prev_order = item->order; + } else if(!sort_required && item->order != prev_order) + { + sort_required = true; + } + + items.emplace_back(item); + } + + if(sort_required) + { + items.sort([](const std::shared_ptr<flex_item>& item1, const std::shared_ptr<flex_item>& item2) + { + if(item1->order < item2->order) return true; + if(item1->order == item2->order) + { + return item1->src_order < item2->src_order; + } + return false; + }); + } + + // Add flex items to lines + for(auto& item : items) + { + if(!line.items.empty() && !single_line && line.base_size + item->base_size > container_main_size) + { + lines.emplace_back(line); + line = flex_line(reverse_main, reverse_cross); + } + line.base_size += item->base_size; + line.total_grow += item->grow; + line.total_shrink += item->shrink; + if(!item->auto_margin_main_start.is_default()) line.num_auto_margin_main_start++; + if(!item->auto_margin_main_end.is_default()) line.num_auto_margin_main_end++; + line.items.push_back(item); + } + // Add the last line to the lines list + if(!line.items.empty()) + { + lines.emplace_back(line); + } + return lines; } std::shared_ptr<litehtml::render_item> litehtml::render_item_flex::init() @@ -81,10 +396,60 @@ std::shared_ptr<litehtml::render_item> litehtml::render_item_flex::init() } convert_inlines(); children() = new_children; - for(const auto& el : children()) - { - m_flex_items.emplace_back(new flex_item(el)); - } return shared_from_this(); } + +int litehtml::render_item_flex::get_first_baseline() +{ + if(css().get_flex_direction() == flex_direction_row || css().get_flex_direction() == flex_direction_row_reverse) + { + if(!m_lines.empty()) + { + const auto &first_line = m_lines.front(); + if(first_line.first_baseline.type() != baseline::baseline_type_none) + { + return first_line.cross_start + first_line.first_baseline.get_offset_from_top(first_line.cross_size) + content_offset_top(); + } + if(first_line.last_baseline.type() != baseline::baseline_type_none) + { + return first_line.cross_start + first_line.last_baseline.get_offset_from_top(first_line.cross_size) + content_offset_top(); + } + } + } + if(!m_lines.empty()) + { + if(!m_lines.front().items.empty()) + { + return m_lines.front().items.front()->el->get_first_baseline() + content_offset_top(); + } + } + return height(); +} + +int litehtml::render_item_flex::get_last_baseline() +{ + if(css().get_flex_direction() == flex_direction_row || css().get_flex_direction() == flex_direction_row_reverse) + { + if(!m_lines.empty()) + { + const auto &first_line = m_lines.front(); + if(first_line.last_baseline.type() != baseline::baseline_type_none) + { + return first_line.cross_start + first_line.last_baseline.get_offset_from_top(first_line.cross_size) + content_offset_top(); + } + if(first_line.first_baseline.type() != baseline::baseline_type_none) + { + return first_line.cross_start + first_line.first_baseline.get_offset_from_top(first_line.cross_size) + content_offset_top(); + } + } + } + if(!m_lines.empty()) + { + if(!m_lines.front().items.empty()) + { + return m_lines.front().items.front()->el->get_last_baseline() + content_offset_top(); + } + } + return height(); +} diff --git a/src/plugins/litehtml_viewer/litehtml/render_flex.h b/src/plugins/litehtml_viewer/litehtml/render_flex.h @@ -2,32 +2,17 @@ #define LITEHTML_RENDER_FLEX_H #include "render_block.h" +#include "flex_item.h" +#include "flex_line.h" namespace litehtml { class render_item_flex : public render_item_block { - struct flex_item - { - std::shared_ptr<render_item> el; - int base_size; - int main_size; - int min_width; - int max_width; - int line; - - explicit flex_item(std::shared_ptr<render_item> _el) : - el(std::move(_el)), - min_width(0), - max_width(0), - line(0), - base_size(0), - main_size(0) - {} - }; - protected: - std::list<std::unique_ptr<flex_item>> m_flex_items; + std::list<flex_line> m_lines; + std::list<flex_line> get_lines(const containing_block_context &self_size, formatting_context *fmt_ctx, bool is_row_direction, + int container_main_size, bool single_line); int _render_content(int x, int y, bool second_pass, const containing_block_context &self_size, formatting_context* fmt_ctx) override; public: @@ -38,8 +23,10 @@ namespace litehtml { return std::make_shared<render_item_flex>(src_el()); } - void draw_children(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex) override; std::shared_ptr<render_item> init() override; + + int get_first_baseline() override; + int get_last_baseline() override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/render_image.cpp b/src/plugins/litehtml_viewer/litehtml/render_image.cpp @@ -5,6 +5,7 @@ int litehtml::render_item_image::_render(int x, int y, const containing_block_context &containing_block_size, formatting_context* fmt_ctx, bool second_pass) { int parent_width = containing_block_size.width; + containing_block_context self_size = calculate_containing_block_context(containing_block_size); calc_outlines(parent_width); @@ -60,10 +61,10 @@ int litehtml::render_item_image::_render(int x, int y, const containing_block_co } } else if(!src_el()->css().get_height().is_predefined() && src_el()->css().get_width().is_predefined()) { - if (!get_predefined_height(m_pos.height, containing_block_size.height)) - { - m_pos.height = (int)src_el()->css().get_height().val(); - } + if(self_size.height.type != containing_block_context::cbc_value_type_auto && self_size.height > 0) + { + m_pos.height = self_size.height; + } // check for max-height if(!src_el()->css().get_max_height().is_predefined()) @@ -107,10 +108,10 @@ int litehtml::render_item_image::_render(int x, int y, const containing_block_co { m_pos.width = (int) src_el()->css().get_width().calc_percent(parent_width); m_pos.height = 0; - if (!get_predefined_height(m_pos.height, containing_block_size.height)) - { - m_pos.height = (int)src_el()->css().get_height().val(); - } + if(self_size.height.type != containing_block_context::cbc_value_type_auto && self_size.height > 0) + { + m_pos.height = self_size.height; + } // check for max-height if(!src_el()->css().get_max_height().is_predefined()) diff --git a/src/plugins/litehtml_viewer/litehtml/render_inline.cpp b/src/plugins/litehtml_viewer/litehtml/render_inline.cpp @@ -1,3 +0,0 @@ -#include "html.h" -#include "render_item.h" - diff --git a/src/plugins/litehtml_viewer/litehtml/render_inline.h b/src/plugins/litehtml_viewer/litehtml/render_inline.h @@ -18,7 +18,14 @@ namespace litehtml void set_inline_boxes( position::vector& boxes ) override { m_boxes = boxes; } void add_inline_box( const position& box ) override { m_boxes.emplace_back(box); }; void clear_inline_boxes() override { m_boxes.clear(); } - int get_base_line() override { return src_el()->css().get_font_metrics().base_line(); } + int get_first_baseline() override + { + return src_el()->css().get_font_metrics().height - src_el()->css().get_font_metrics().base_line(); + } + int get_last_baseline() override + { + return src_el()->css().get_font_metrics().height - src_el()->css().get_font_metrics().base_line(); + } std::shared_ptr<render_item> clone() override { @@ -28,3 +35,4 @@ namespace litehtml } #endif //LITEHTML_RENDER_INLINE_H + diff --git a/src/plugins/litehtml_viewer/litehtml/render_inline_context.cpp b/src/plugins/litehtml_viewer/litehtml/render_inline_context.cpp @@ -382,21 +382,30 @@ void litehtml::render_item_inline_context::apply_vertical_align() } } -int litehtml::render_item_inline_context::get_base_line() +int litehtml::render_item_inline_context::get_first_baseline() { - auto el_parent = parent(); - if(el_parent && src_el()->css().get_display() == display_inline_flex) - { - return el_parent->get_base_line(); - } - if(src_el()->is_replaced()) - { - return 0; - } - int bl = 0; - if(!m_line_boxes.empty()) - { - bl = m_line_boxes.back()->baseline() + content_offset_bottom(); - } - return bl; + int bl; + if(!m_line_boxes.empty()) + { + const auto &line = m_line_boxes.front(); + bl = line->bottom() - line->baseline() + content_offset_top(); + } else + { + bl = height() - margin_bottom(); + } + return bl; +} + +int litehtml::render_item_inline_context::get_last_baseline() +{ + int bl; + if(!m_line_boxes.empty()) + { + const auto &line = m_line_boxes.back(); + bl = line->bottom() - line->baseline() + content_offset_top(); + } else + { + bl = height() - margin_bottom(); + } + return bl; } diff --git a/src/plugins/litehtml_viewer/litehtml/render_inline_context.h b/src/plugins/litehtml_viewer/litehtml/render_inline_context.h @@ -48,7 +48,8 @@ namespace litehtml return std::make_shared<render_item_inline_context>(src_el()); } - int get_base_line() override; + int get_first_baseline() override; + int get_last_baseline() override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/render_item.cpp b/src/plugins/litehtml_viewer/litehtml/render_item.cpp @@ -138,37 +138,6 @@ void litehtml::render_item::apply_relative_shift(const containing_block_context } } -bool litehtml::render_item::get_predefined_height(int& p_height, int containing_block_height) const -{ - css_length h = src_el()->css().get_height(); - if(h.is_predefined()) - { - p_height = m_pos.height; - return false; - } - if(h.units() == css_units_percentage) - { - p_height = h.calc_percent(containing_block_height); - return containing_block_height > 0; - } - p_height = src_el()->get_document()->to_pixels(h, src_el()->css().get_font_size()); - return p_height > 0; -} - -int litehtml::render_item::calc_width(int defVal, int containing_block_width) const -{ - css_length w = src_el()->css().get_width(); - if(w.is_predefined() || src_el()->css().get_display() == display_table_cell) - { - return defVal; - } - if(w.units() == css_units_percentage) - { - return w.calc_percent(containing_block_width); - } - return src_el()->get_document()->to_pixels(w, src_el()->css().get_font_size()); -} - std::tuple< std::shared_ptr<litehtml::render_item>, std::shared_ptr<litehtml::render_item>, @@ -679,7 +648,7 @@ void litehtml::render_item::draw_children(uint_ptr hdc, int x, int y, const posi if (el->src_el()->is_inline() && el->src_el()->css().get_float() == float_none && !el->src_el()->is_positioned()) { el->src_el()->draw(hdc, pos.x, pos.y, clip, el); - if (el->src_el()->css().get_display() == display_inline_block) + if (el->src_el()->css().get_display() == display_inline_block || el->src_el()->css().get_display() == display_inline_flex) { el->draw_stacking_context(hdc, pos.x, pos.y, clip, false); process = false; @@ -816,7 +785,7 @@ std::shared_ptr<litehtml::element> litehtml::render_item::get_child_by_point(in } else { if( el->src_el()->css().get_float() == float_none && - el->src_el()->css().get_display() != display_inline_block) + el->src_el()->css().get_display() != display_inline_block && el->src_el()->css().get_display() != display_inline_flex) { element::ptr child = el->get_child_by_point(el_pos.x, el_pos.y, client_x, client_y, flag, zindex); if(child) @@ -845,25 +814,23 @@ std::shared_ptr<litehtml::element> litehtml::render_item::get_element_by_point(i z_indexes[i->src_el()->css().get_z_index()]; } - for(const auto& zindex : z_indexes) + for(auto iter = z_indexes.rbegin(); iter != z_indexes.rend(); iter++) { - if(zindex.first > 0) + if(iter->first > 0) { - ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, zindex.first); - break; + ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, iter->first); + if(ret) return ret; } } - if(ret) return ret; for(const auto& z_index : z_indexes) { if(z_index.first == 0) { ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, z_index.first); - break; + if(ret) return ret; } } - if(ret) return ret; ret = get_child_by_point(x, y, client_x, client_y, draw_inlines, 0); if(ret) return ret; @@ -875,15 +842,14 @@ std::shared_ptr<litehtml::element> litehtml::render_item::get_element_by_point(i if(ret) return ret; - for(const auto& z_index : z_indexes) - { - if(z_index.first < 0) + for(auto iter = z_indexes.rbegin(); iter != z_indexes.rend(); iter++) + { + if(iter->first < 0) { - ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, z_index.first); - break; + ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, iter->first); + if(ret) return ret; } } - if(ret) return ret; if(src_el()->css().get_position() == element_position_fixed) { @@ -1042,8 +1008,55 @@ litehtml::containing_block_context litehtml::render_item::calculate_containing_b // We have to use aut value for display_table_cell also. if (src_el()->css().get_display() != display_table_cell) { - calc_cb_length(src_el()->css().get_width(), cb_context.width, ret.width); - calc_cb_length(src_el()->css().get_height(), cb_context.height, ret.height); + auto par = parent(); + if(cb_context.size_mode & containing_block_context::size_mode_exact_width) + { + ret.width.value = cb_context.width; + ret.width.type = containing_block_context::cbc_value_type_absolute; + } else + { + auto *width = &css().get_width(); + if(par && (par->css().get_display() == display_flex || par->css().get_display() == display_inline_flex)) + { + if(!css().get_flex_basis().is_predefined() && css().get_flex_basis().val() >= 0) + { + if(par->css().get_flex_direction() == flex_direction_row || par->css().get_flex_direction() == flex_direction_row_reverse) + { + ret.width.type = containing_block_context::cbc_value_type_auto; + ret.width.value = 0; + width = nullptr; + } + } + } + if(width) + { + calc_cb_length(*width, cb_context.width, ret.width); + } + } + if(cb_context.size_mode & containing_block_context::size_mode_exact_height) + { + ret.height.value = cb_context.height; + ret.height.type = containing_block_context::cbc_value_type_absolute; + } else + { + auto *height = &css().get_height(); + if(par && (par->css().get_display() == display_flex || par->css().get_display() == display_inline_flex)) + { + if(!css().get_flex_basis().is_predefined() && css().get_flex_basis().val() >= 0) + { + if(par->css().get_flex_direction() == flex_direction_column || par->css().get_flex_direction() == flex_direction_column_reverse) + { + ret.height.type = containing_block_context::cbc_value_type_auto; + ret.height.value = 0; + height = nullptr; + } + } + } + if(height) + { + calc_cb_length(*height, cb_context.height, ret.height); + } + } if (ret.width.type != containing_block_context::cbc_value_type_auto && (src_el()->css().get_display() == display_table || src_el()->is_root())) { ret.width.value -= content_offset_width(); @@ -1061,28 +1074,27 @@ litehtml::containing_block_context litehtml::render_item::calculate_containing_b calc_cb_length(src_el()->css().get_min_height(), cb_context.height, ret.min_height); calc_cb_length(src_el()->css().get_max_height(), cb_context.height, ret.max_height); - if (src_el()->css().get_box_sizing() == box_sizing_border_box) + // Fix box sizing + if(ret.width.type != containing_block_context::cbc_value_type_auto) { - if(ret.width.type != containing_block_context::cbc_value_type_auto) - { - ret.render_width = ret.width - box_sizing_width(); - } - if(ret.min_width.type != containing_block_context::cbc_value_type_none) - { - ret.min_width.value -= box_sizing_width(); - } - if(ret.max_width.type != containing_block_context::cbc_value_type_none) - { - ret.max_width.value -= box_sizing_width(); - } - if(ret.min_height.type != containing_block_context::cbc_value_type_none) - { - ret.min_height.value -= box_sizing_height(); - } - if(ret.max_height.type != containing_block_context::cbc_value_type_none) - { - ret.max_height.value -= box_sizing_height(); - } + ret.render_width = ret.width - box_sizing_width(); + } + if(ret.min_width.type != containing_block_context::cbc_value_type_none) + { + ret.min_width.value -= box_sizing_width(); } + if(ret.max_width.type != containing_block_context::cbc_value_type_none) + { + ret.max_width.value -= box_sizing_width(); + } + if(ret.min_height.type != containing_block_context::cbc_value_type_none) + { + ret.min_height.value -= box_sizing_height(); + } + if(ret.max_height.type != containing_block_context::cbc_value_type_none) + { + ret.max_height.value -= box_sizing_height(); + } + return ret; } diff --git a/src/plugins/litehtml_viewer/litehtml/render_item.h b/src/plugins/litehtml_viewer/litehtml/render_item.h @@ -86,7 +86,7 @@ namespace litehtml int width() const { - return m_pos.width + m_margins.left + m_margins.right + m_padding.width() + m_borders.width(); + return m_pos.width + m_margins.width() + m_padding.width() + m_borders.width(); } int padding_top() const @@ -224,27 +224,43 @@ namespace litehtml int box_sizing_left() const { - return m_padding.left + m_borders.left; + if(css().get_box_sizing() == box_sizing_border_box) + { + return m_padding.left + m_borders.left; + } + return 0; } int box_sizing_right() const { - return m_padding.right + m_borders.right; + if(css().get_box_sizing() == box_sizing_border_box) + { + return m_padding.right + m_borders.right; + } + return 0; } int box_sizing_width() const { - return box_sizing_left() + box_sizing_left(); + return box_sizing_left() + box_sizing_right(); } int box_sizing_top() const { - return m_padding.top + m_borders.top; + if(css().get_box_sizing() == box_sizing_border_box) + { + return m_padding.top + m_borders.top; + } + return 0; } int box_sizing_bottom() const { - return m_padding.bottom + m_borders.bottom; + if(css().get_box_sizing() == box_sizing_border_box) + { + return m_padding.bottom + m_borders.bottom; + } + return 0; } int box_sizing_height() const @@ -285,6 +301,7 @@ namespace litehtml m_element->in_normal_flow() && m_element->css().get_float() == float_none && m_margins.top >= 0 && + !is_flex_item() && !is_root(); } @@ -303,16 +320,34 @@ namespace litehtml return !(m_skip || src_el()->css().get_display() == display_none || src_el()->css().get_visibility() != visibility_visible); } + bool is_flex_item() const + { + auto par = parent(); + if(par && (par->css().get_display() == display_inline_flex || par->css().get_display() == display_flex)) + { + return true; + } + return false; + } + int render(int x, int y, const containing_block_context& containing_block_size, formatting_context* fmt_ctx, bool second_pass = false); - int calc_width(int defVal, int containing_block_width) const; - bool get_predefined_height(int& p_height, int containing_block_height) const; void apply_relative_shift(const containing_block_context &containing_block_size); void calc_outlines( int parent_width ); int calc_auto_margins(int parent_width); // returns left margin virtual std::shared_ptr<render_item> init(); virtual void apply_vertical_align() {} - virtual int get_base_line() { return 0; } + /** + * Get first baseline position. Default position is element bottom without bottom margin. + * @returns offset of the first baseline from element top + */ + virtual int get_first_baseline() { return height() - margin_bottom(); } + /** + * Get last baseline position. Default position is element bottom without bottom margin. + * @returns offset of the last baseline from element top + */ + virtual int get_last_baseline() { return height() - margin_bottom(); } + virtual std::shared_ptr<render_item> clone() { return std::make_shared<render_item>(src_el()); diff --git a/src/plugins/litehtml_viewer/litehtml/render_table.cpp b/src/plugins/litehtml_viewer/litehtml/render_table.cpp @@ -261,10 +261,10 @@ int litehtml::render_item_table::_render(int x, int y, const containing_block_co // calculate block height int block_height = 0; - if (get_predefined_height(block_height, containing_block_size.height)) - { - block_height -= m_padding.height() + m_borders.height(); - } + if(self_size.height.type != containing_block_context::cbc_value_type_auto && self_size.height > 0) + { + block_height = self_size.height - (m_padding.height() + m_borders.height()); + } // calculate minimum height from m_css.get_min_height() int min_height = 0; diff --git a/src/plugins/litehtml_viewer/litehtml/string_id.h b/src/plugins/litehtml_viewer/litehtml/string_id.h @@ -289,6 +289,10 @@ STRING_ID( _flex_basis_, _caption_side_, + _order_, + + _counter_reset_, + _counter_increment_, ); #undef STRING_ID extern const string_id empty_id; // _id("") diff --git a/src/plugins/litehtml_viewer/litehtml/style.cpp b/src/plugins/litehtml_viewer/litehtml/style.cpp @@ -43,7 +43,7 @@ std::map<string_id, string> style::m_valid_values = { _justify_content_, flex_justify_content_strings }, { _align_items_, flex_align_items_strings }, { _align_content_, flex_align_content_strings }, - { _align_self_, flex_align_self_strings }, + { _align_self_, flex_align_items_strings }, { _caption_side_, caption_side_strings }, }; @@ -128,9 +128,7 @@ void style::add_property(string_id name, const string& val, const string& baseur case _flex_direction_: case _flex_wrap_: case _justify_content_: - case _align_items_: case _align_content_: - case _align_self_: case _caption_side_: @@ -141,6 +139,11 @@ void style::add_property(string_id name, const string& val, const string& baseur } break; + case _align_items_: + case _align_self_: + parse_align_self(name, val, important); + break; + // <length> case _text_indent_: case _padding_left_: @@ -536,6 +539,25 @@ void style::add_property(string_id name, const string& val, const string& baseur add_parsed_property(_flex_basis_, property_value(length, important)); break; + case _order_: // <integer> + { + char* end; + int int_val = (int) strtol(val.c_str(), &end, 10); + if(end[0] == '\0') + { + add_parsed_property(name, property_value(int_val, important)); + } + } + break; + case _counter_increment_: + case _counter_reset_: + { + string_vector tokens; + split_string(val, tokens, " "); + add_parsed_property(name, property_value(tokens, important)); + break; + } + default: add_parsed_property(name, property_value(val, important)); } @@ -972,14 +994,16 @@ void style::parse_font(const string& val, bool important) { string_vector szlh; split_string(token, szlh, "/"); - - auto size = css_length::from_string(szlh[0], font_size_strings, -1); - add_parsed_property(_font_size_, property_value(size, important)); - - if(szlh.size() == 2) + if(!szlh.empty()) { - auto height = css_length::from_string(szlh[1], "normal", -1); - add_parsed_property(_line_height_, property_value(height, important)); + auto size = css_length::from_string(szlh[0], font_size_strings, -1); + add_parsed_property(_font_size_, property_value(size, important)); + + if (szlh.size() == 2) + { + auto height = css_length::from_string(szlh[1], "normal", -1); + add_parsed_property(_line_height_, property_value(height, important)); + } } } else { @@ -992,18 +1016,6 @@ void style::parse_font(const string& val, bool important) void style::parse_flex(const string& val, bool important) { - auto is_number = [](const string& val) - { - for (auto ch : val) - { - if ((ch < '0' || ch > '9') && ch != '.') - { - return false; - } - } - return true; - }; - css_length _auto = css_length::predef_value(flex_basis_auto); if (val == "initial") @@ -1036,6 +1048,10 @@ void style::parse_flex(const string& val, bool important) float grow = t_strtof(tokens[0]); float shrink = t_strtof(tokens[1]); auto basis = css_length::from_string(tokens[2], flex_basis_strings, -1); + if(!basis.is_predefined() && basis.units() == css_units_none && basis.val() == 0) + { + basis.set_value(basis.val(), css_units_px); + } add_parsed_property(_flex_grow_, property_value(grow, important)); add_parsed_property(_flex_shrink_, property_value(shrink, important)); @@ -1046,10 +1062,11 @@ void style::parse_flex(const string& val, bool important) float grow = t_strtof(tokens[0]); add_parsed_property(_flex_grow_, property_value(grow, important)); - if (is_number(tokens[1])) + if (litehtml::is_number(tokens[1])) { float shrink = t_strtof(tokens[1]); add_parsed_property(_flex_shrink_, property_value(shrink, important)); + add_parsed_property(_flex_basis_, property_value(css_length(0), important)); } else { @@ -1063,22 +1080,65 @@ void style::parse_flex(const string& val, bool important) { float grow = t_strtof(tokens[0]); add_parsed_property(_flex_grow_, property_value(grow, important)); - - if (grow >= 1) - { - add_parsed_property(_flex_shrink_, property_value(1.f, important)); - add_parsed_property(_flex_basis_, property_value(css_length(0), important)); - } + add_parsed_property(_flex_shrink_, property_value(1.f, important)); + add_parsed_property(_flex_basis_, property_value(css_length(0), important)); } else { auto basis = css_length::from_string(tokens[0], flex_basis_strings, -1); + add_parsed_property(_flex_grow_, property_value(1.f, important)); + add_parsed_property(_flex_shrink_, property_value(1.f, important)); add_parsed_property(_flex_basis_, property_value(basis, important)); } } } } +void style::parse_align_self(string_id name, const string& val, bool important) +{ + string_vector tokens; + split_string(val, tokens, " "); + if(tokens.size() == 1) + { + int idx = value_index(val, m_valid_values[name]); + if (idx >= 0) + { + add_parsed_property(name, property_value(idx, important)); + } + } else + { + int val1 = 0; + int val2 = -1; + for(auto &token : tokens) + { + if(token == "first") + { + val1 |= flex_align_items_first; + } else if(token == "last") + { + val1 |= flex_align_items_last; + } else if(token == "safe") + { + val1 |= flex_align_items_safe; + } else if(token == "unsafe") + { + val1 |= flex_align_items_unsafe; + } else + { + int idx = value_index(token, m_valid_values[name]); + if(idx >= 0) + { + val2 = idx; + } + } + } + if(val2 >= 0) + { + add_parsed_property(name, property_value(val1 | val2, important)); + } + } +} + void style::add_parsed_property( string_id name, const property_value& propval ) { auto prop = m_properties.find(name); diff --git a/src/plugins/litehtml_viewer/litehtml/style.h b/src/plugins/litehtml_viewer/litehtml/style.h @@ -199,6 +199,7 @@ namespace litehtml bool parse_one_background_size(const string& val, css_size& size); void parse_font(const string& val, bool important); void parse_flex(const string& val, bool important); + void parse_align_self(string_id name, const string& val, bool important); static css_length parse_border_width(const string& str); static void parse_two_lengths(const string& str, css_length len[2]); static int parse_four_lengths(const string& str, css_length len[4]); diff --git a/src/plugins/litehtml_viewer/litehtml/types.h b/src/plugins/litehtml_viewer/litehtml/types.h @@ -195,6 +195,14 @@ namespace litehtml cbc_value_type_none, // min/max width/height of containing block is defined as none }; + enum cbc_size_mode + { + size_mode_normal = 0x00, + size_mode_exact_width = 0x01, + size_mode_exact_height = 0x02, + size_mode_content = 0x04, + }; + struct typed_int { int value; @@ -235,6 +243,7 @@ namespace litehtml typed_int max_height; int context_idx; + uint32_t size_mode; containing_block_context() : width(0, cbc_value_type_auto), @@ -244,17 +253,26 @@ namespace litehtml height(0, cbc_value_type_auto), min_height(0, cbc_value_type_none), max_height(0, cbc_value_type_none), - context_idx(0) + context_idx(0), + size_mode(size_mode_normal) {} - containing_block_context new_width(int w) const + containing_block_context new_width(int w, uint32_t _size_mode = size_mode_normal) const { containing_block_context ret = *this; - //if(ret.width.type != cbc_value_type_absolute) - { - ret.render_width = w - (ret.width - ret.render_width); - ret.width = w; - } + ret.render_width = w - (ret.width - ret.render_width); + ret.width = w; + ret.size_mode = _size_mode; + return ret; + } + + containing_block_context new_width_height(int w, int h, uint32_t _size_mode = size_mode_normal) const + { + containing_block_context ret = *this; + ret.render_width = w - (ret.width - ret.render_width); + ret.width = w; + ret.height = h; + ret.size_mode = _size_mode; return ret; } }; @@ -693,7 +711,7 @@ namespace litehtml m_is_default = true; m_val = def_val; } - bool is_default() + bool is_default() const { return m_is_default; } @@ -703,12 +721,72 @@ namespace litehtml m_is_default = false; return m_val; } - operator T() + operator T() const { return m_val; } }; + class baseline + { + public: + enum _baseline_type + { + baseline_type_none, + baseline_type_top, + baseline_type_bottom, + }; + + public: + baseline() : m_value(0), m_type(baseline_type_none) {} + baseline(int _value, _baseline_type _type) : m_value(_value), m_type(_type) {} + + int value() const { return m_value; } + void value(int _value) { m_value = _value; } + _baseline_type type() const { return m_type; } + void type(_baseline_type _type) { m_type = _type; } + + operator int() const { return m_value; } + baseline& operator=(int _value) { m_value = _value; return *this; } + + void set(int _value, _baseline_type _type) { m_value = _value; m_type =_type; } + /** + * Get baseline offset from top of element with specified height + * @param height - element height + * @return baseline offset + */ + int get_offset_from_top(int height) const + { + if(m_type == baseline_type_top) return m_value; + return height - m_value; + } + /** + * Get baseline offset from bottom of element with specified height + * @param height - element height + * @return baseline offset + */ + int get_offset_from_bottom(int height) const + { + if(m_type == baseline_type_bottom) return m_value; + return height - m_value; + } + /** + * Calculate baseline by top and bottom positions of element aligned by baseline == 0 + * @param top - top of the aligned element + * @param bottom - bottom of the aligned element + */ + void calc(int top, int bottom) + { + if(m_type == baseline_type_top) + m_value = -top; + else if(m_type == baseline_type_bottom) + m_value = bottom; + } + private: + int m_value; + _baseline_type m_type; + }; + #define media_orientation_strings "portrait;landscape" @@ -848,46 +926,51 @@ namespace litehtml flex_wrap_wrap_reverse }; -#define flex_justify_content_strings "flex-start;flex-end;center;space-between;space-around" +#define flex_justify_content_strings "normal;flex-start;flex-end;center;space-between;space-around;start;end;left;right;space-evenly;stretch" enum flex_justify_content { + flex_justify_content_normal, flex_justify_content_flex_start, flex_justify_content_flex_end, flex_justify_content_center, flex_justify_content_space_between, - flex_justify_content_space_around + flex_justify_content_space_around, + flex_justify_content_start, + flex_justify_content_end, + flex_justify_content_left, + flex_justify_content_right, + flex_justify_content_space_evenly, + flex_justify_content_stretch, }; -#define flex_align_items_strings "flex-start;flex-end;center;baseline;stretch" +#define flex_align_items_strings "normal;flex-start;flex-end;center;start;end;baseline;stretch;auto" enum flex_align_items { + flex_align_items_flex_normal, flex_align_items_flex_start, flex_align_items_flex_end, flex_align_items_center, + flex_align_items_start, + flex_align_items_end, flex_align_items_baseline, - flex_align_items_stretch - }; - -#define flex_align_self_strings "auto;flex-start;flex-end;center;baseline;stretch" - - enum flex_align_self - { - flex_align_self_auto, - flex_align_self_flex_start, - flex_align_self_flex_end, - flex_align_self_center, - flex_align_self_baseline, - flex_align_self_stretch + flex_align_items_stretch, + flex_align_items_auto, // used for align-self property only + flex_align_items_first = 0x100, + flex_align_items_last = 0x200, + flex_align_items_unsafe = 0x400, + flex_align_items_safe = 0x800, }; -#define flex_align_content_strings "flex-start;flex-end;center;space-between;space-around;stretch" +#define flex_align_content_strings "flex-start;start;flex-end;end;center;space-between;space-around;stretch" enum flex_align_content { flex_align_content_flex_start, + flex_align_content_start, flex_align_content_flex_end, + flex_align_content_end, flex_align_content_center, flex_align_content_space_between, flex_align_content_space_around,