talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

commit cf2e2a3236893511b498412b0f1f79f91c962d29
parent 99db9602c30d19ba5e68b5c87b45c41166c74d46
Author: Jonathan Boeing <jonathan@claws-mail.org>
Date:   Mon,  9 Oct 2023 00:28:03 -0700

Sync with litehtml v0.7

Diffstat:
Msrc/plugins/litehtml_viewer/litehtml/Makefile.am | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Dsrc/plugins/litehtml_viewer/litehtml/attributes.h | 35-----------------------------------
Dsrc/plugins/litehtml_viewer/litehtml/background.cpp | 79-------------------------------------------------------------------------------
Msrc/plugins/litehtml_viewer/litehtml/background.h | 51+++++++++++++++++++++++++++++++--------------------
Msrc/plugins/litehtml_viewer/litehtml/borders.h | 28+++++++++++++++++++++-------
Dsrc/plugins/litehtml_viewer/litehtml/box.cpp | 434-------------------------------------------------------------------------------
Dsrc/plugins/litehtml_viewer/litehtml/box.h | 120-------------------------------------------------------------------------------
Asrc/plugins/litehtml_viewer/litehtml/codepoint.cpp | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/codepoint.h | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/plugins/litehtml_viewer/litehtml/context.cpp | 12------------
Dsrc/plugins/litehtml_viewer/litehtml/context.h | 20--------------------
Asrc/plugins/litehtml_viewer/litehtml/css_borders.cpp | 7+++++++
Msrc/plugins/litehtml_viewer/litehtml/css_length.cpp | 47+++++++++++++++++++++++++++++++++++------------
Msrc/plugins/litehtml_viewer/litehtml/css_length.h | 42+++++++++++++-----------------------------
Msrc/plugins/litehtml_viewer/litehtml/css_margins.h | 13+++++++++----
Msrc/plugins/litehtml_viewer/litehtml/css_offsets.h | 13+++++++++----
Msrc/plugins/litehtml_viewer/litehtml/css_position.h | 29+++++++++--------------------
Asrc/plugins/litehtml_viewer/litehtml/css_properties.cpp | 456+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/css_properties.h | 643+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/plugins/litehtml_viewer/litehtml/css_selector.cpp | 261+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/plugins/litehtml_viewer/litehtml/css_selector.h | 56+++++++++++++++++++++++++++++++++++++++-----------------
Msrc/plugins/litehtml_viewer/litehtml/document.cpp | 587+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/plugins/litehtml_viewer/litehtml/document.h | 95+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Asrc/plugins/litehtml_viewer/litehtml/document_container.cpp | 44++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/document_container.h | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/plugins/litehtml_viewer/litehtml/el_anchor.cpp | 11+++--------
Msrc/plugins/litehtml_viewer/litehtml/el_anchor.h | 7+++----
Msrc/plugins/litehtml_viewer/litehtml/el_base.cpp | 9++-------
Msrc/plugins/litehtml_viewer/litehtml/el_base.h | 5++---
Msrc/plugins/litehtml_viewer/litehtml/el_before_after.cpp | 146+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/plugins/litehtml_viewer/litehtml/el_before_after.h | 17++++++++---------
Msrc/plugins/litehtml_viewer/litehtml/el_body.cpp | 5-----
Msrc/plugins/litehtml_viewer/litehtml/el_body.h | 5++---
Msrc/plugins/litehtml_viewer/litehtml/el_break.cpp | 5-----
Msrc/plugins/litehtml_viewer/litehtml/el_break.h | 7+++----
Msrc/plugins/litehtml_viewer/litehtml/el_cdata.cpp | 13++++---------
Msrc/plugins/litehtml_viewer/litehtml/el_cdata.h | 9++++-----
Msrc/plugins/litehtml_viewer/litehtml/el_comment.cpp | 12++++++------
Msrc/plugins/litehtml_viewer/litehtml/el_comment.h | 15++++++++++-----
Msrc/plugins/litehtml_viewer/litehtml/el_div.cpp | 11+++--------
Msrc/plugins/litehtml_viewer/litehtml/el_div.h | 5++---
Msrc/plugins/litehtml_viewer/litehtml/el_font.cpp | 33+++++++++++++++------------------
Msrc/plugins/litehtml_viewer/litehtml/el_font.h | 5++---
Msrc/plugins/litehtml_viewer/litehtml/el_image.cpp | 233++++++++++++++-----------------------------------------------------------------
Msrc/plugins/litehtml_viewer/litehtml/el_image.h | 23++++++++++++-----------
Dsrc/plugins/litehtml_viewer/litehtml/el_li.cpp | 38--------------------------------------
Dsrc/plugins/litehtml_viewer/litehtml/el_li.h | 21---------------------
Msrc/plugins/litehtml_viewer/litehtml/el_link.cpp | 19+++++++------------
Msrc/plugins/litehtml_viewer/litehtml/el_link.h | 5++---
Msrc/plugins/litehtml_viewer/litehtml/el_para.cpp | 11+++--------
Msrc/plugins/litehtml_viewer/litehtml/el_para.h | 5++---
Msrc/plugins/litehtml_viewer/litehtml/el_script.cpp | 16++++++++--------
Msrc/plugins/litehtml_viewer/litehtml/el_script.h | 12++++++------
Msrc/plugins/litehtml_viewer/litehtml/el_space.cpp | 23++++++++++++++---------
Msrc/plugins/litehtml_viewer/litehtml/el_space.h | 9+++++----
Msrc/plugins/litehtml_viewer/litehtml/el_style.cpp | 20++++++++++----------
Msrc/plugins/litehtml_viewer/litehtml/el_style.h | 10+++++-----
Msrc/plugins/litehtml_viewer/litehtml/el_table.cpp | 80++++++++++++++++++++++---------------------------------------------------------
Msrc/plugins/litehtml_viewer/litehtml/el_table.h | 8+++-----
Msrc/plugins/litehtml_viewer/litehtml/el_td.cpp | 32+++++++++++++-------------------
Msrc/plugins/litehtml_viewer/litehtml/el_td.h | 5++---
Msrc/plugins/litehtml_viewer/litehtml/el_text.cpp | 166++++++++++++++++++++++++++++---------------------------------------------------
Msrc/plugins/litehtml_viewer/litehtml/el_text.h | 27++++++++++-----------------
Msrc/plugins/litehtml_viewer/litehtml/el_title.cpp | 10+++-------
Msrc/plugins/litehtml_viewer/litehtml/el_title.h | 5++---
Msrc/plugins/litehtml_viewer/litehtml/el_tr.cpp | 37+++++++------------------------------
Msrc/plugins/litehtml_viewer/litehtml/el_tr.h | 6++----
Msrc/plugins/litehtml_viewer/litehtml/element.cpp | 553++++++++++++++++++++++++++++++++++---------------------------------------------
Msrc/plugins/litehtml_viewer/litehtml/element.h | 398+++++++++++++++++++++----------------------------------------------------------
Msrc/plugins/litehtml_viewer/litehtml/html.cpp | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/plugins/litehtml_viewer/litehtml/html.h | 95++++++++++++++++++++++++++++++++++---------------------------------------------
Msrc/plugins/litehtml_viewer/litehtml/html_tag.cpp | 4948+++++++++++++++----------------------------------------------------------------
Msrc/plugins/litehtml_viewer/litehtml/html_tag.h | 302+++++++++++++++++++++++++------------------------------------------------------
Msrc/plugins/litehtml_viewer/litehtml/iterators.cpp | 111++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/plugins/litehtml_viewer/litehtml/iterators.h | 88+++++++++++++++++++++++++++++++++++++------------------------------------------
Asrc/plugins/litehtml_viewer/litehtml/line_box.cpp | 698+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/line_box.h | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/plugins/litehtml_viewer/litehtml/litehtml.h | 1+
Asrc/plugins/litehtml_viewer/litehtml/master_css.h | 351+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/plugins/litehtml_viewer/litehtml/media_query.cpp | 61++++++++++++++++++++++++++++++-------------------------------
Msrc/plugins/litehtml_viewer/litehtml/media_query.h | 4++--
Msrc/plugins/litehtml_viewer/litehtml/num_cvt.cpp | 63+++++++++++++++++++++++++++++++--------------------------------
Msrc/plugins/litehtml_viewer/litehtml/num_cvt.h | 11+++++------
Msrc/plugins/litehtml_viewer/litehtml/os_types.h | 86+++++++++++--------------------------------------------------------------------
Asrc/plugins/litehtml_viewer/litehtml/render_block.cpp | 856+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/render_block_context.cpp | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/render_flex.cpp | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/render_image.cpp | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/render_inline.cpp | 3+++
Asrc/plugins/litehtml_viewer/litehtml/render_inline_context.cpp | 403+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/render_item.cpp | 1076+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/render_item.h | 599+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/render_table.cpp | 482+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/string_id.cpp | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/string_id.h | 300+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/strtod.cpp | 275+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/plugins/litehtml_viewer/litehtml/style.cpp | 1434+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/plugins/litehtml_viewer/litehtml/style.h | 215+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/plugins/litehtml_viewer/litehtml/stylesheet.cpp | 123++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/plugins/litehtml_viewer/litehtml/stylesheet.h | 23++++++++---------------
Msrc/plugins/litehtml_viewer/litehtml/table.cpp | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/plugins/litehtml_viewer/litehtml/table.h | 65++++++++++++++++++++++++++++++++++-------------------------------
Asrc/plugins/litehtml_viewer/litehtml/tstring_view.cpp | 47+++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/tstring_view.h | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/plugins/litehtml_viewer/litehtml/types.h | 363+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Asrc/plugins/litehtml_viewer/litehtml/url.cpp | 163+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/url.h | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/url_path.cpp | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/plugins/litehtml_viewer/litehtml/url_path.h | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/plugins/litehtml_viewer/litehtml/utf8_strings.cpp | 2++
Msrc/plugins/litehtml_viewer/litehtml/utf8_strings.h | 12++++--------
Msrc/plugins/litehtml_viewer/litehtml/web_color.cpp | 382+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/plugins/litehtml_viewer/litehtml/web_color.h | 50+++++++++++++++++++++-----------------------------
113 files changed, 12333 insertions(+), 8166 deletions(-)

diff --git a/src/plugins/litehtml_viewer/litehtml/Makefile.am b/src/plugins/litehtml_viewer/litehtml/Makefile.am @@ -16,50 +16,113 @@ endif liblitehtml_la_CXXFLAGS = -std=c++11 liblitehtml_la_SOURCES = \ - attributes.h \ - background.cpp background.h \ + background.h \ borders.h \ - box.cpp box.h \ - context.cpp context.h \ - css_length.cpp css_length.h \ - css_margins.h css_offsets.h css_position.h \ - css_selector.cpp css_selector.h \ - document.cpp document.h \ - el_anchor.cpp el_anchor.h \ - el_base.cpp el_base.h \ - el_before_after.cpp el_before_after.h \ - el_body.cpp el_body.h \ - el_break.cpp el_break.h \ - el_cdata.cpp el_cdata.h \ - el_comment.cpp el_comment.h \ - el_div.cpp el_div.h \ - element.cpp element.h \ - el_font.cpp el_font.h \ - el_image.cpp el_image.h \ - el_li.cpp el_li.h \ - el_link.cpp el_link.h \ - el_para.cpp el_para.h \ - el_script.cpp el_script.h \ - el_space.cpp el_space.h \ - el_style.cpp el_style.h \ - el_table.cpp el_table.h \ - el_td.cpp el_td.h \ - el_text.cpp el_text.h \ - el_title.cpp el_title.h \ - el_tr.cpp el_tr.h \ - html.cpp html.h \ - html_tag.cpp html_tag.h \ - iterators.cpp iterators.h \ + codepoint.cpp \ + codepoint.h \ + css_borders.cpp \ + css_length.cpp \ + css_length.h \ + css_margins.h \ + css_offsets.h \ + css_position.h \ + css_properties.cpp \ + css_properties.h \ + css_selector.cpp \ + css_selector.h \ + document_container.cpp \ + document_container.h \ + document.cpp \ + document.h \ + el_anchor.cpp \ + el_anchor.h \ + el_base.cpp \ + el_base.h \ + el_before_after.cpp \ + el_before_after.h \ + el_body.cpp \ + el_body.h \ + el_break.cpp \ + el_break.h \ + el_cdata.cpp \ + el_cdata.h \ + el_comment.cpp \ + el_comment.h \ + el_div.cpp \ + el_div.h \ + element.cpp \ + element.h \ + el_font.cpp \ + el_font.h \ + el_image.cpp \ + el_image.h \ + el_link.cpp \ + el_link.h \ + el_para.cpp \ + el_para.h \ + el_script.cpp \ + el_script.h \ + el_space.cpp \ + el_space.h \ + el_style.cpp \ + el_style.h \ + el_table.cpp \ + el_table.h \ + el_td.cpp \ + el_td.h \ + el_text.cpp \ + el_text.h \ + el_title.cpp \ + el_title.h \ + el_tr.cpp \ + el_tr.h \ + html.cpp \ + html.h \ + html_tag.cpp \ + html_tag.h \ + iterators.cpp \ + iterators.h \ + LICENSE \ + line_box.cpp \ + line_box.h \ litehtml.h \ - media_query.cpp media_query.h \ - num_cvt.cpp num_cvt.h \ + Makefile.am \ + master_css.h \ + media_query.cpp \ + media_query.h \ + num_cvt.cpp \ + num_cvt.h \ os_types.h \ - style.cpp style.h \ - stylesheet.cpp stylesheet.h \ - table.cpp table.h \ + README.md \ + render_block_context.cpp \ + render_block.cpp \ + render_flex.cpp \ + render_image.cpp \ + render_inline_context.cpp \ + render_inline.cpp \ + render_item.cpp \ + render_item.h \ + render_table.cpp \ + string_id.cpp \ + string_id.h \ + strtod.cpp \ + style.cpp \ + style.h \ + stylesheet.cpp \ + stylesheet.h \ + table.cpp \ + table.h \ + tstring_view.cpp \ + tstring_view.h \ types.h \ - utf8_strings.cpp utf8_strings.h \ - web_color.cpp web_color.h + url.cpp \ + url.h \ + url_path.cpp \ + url_path.h \ + utf8_strings.cpp \ + utf8_strings.h \ + web_color.cpp \ + web_color.h liblitehtml_la_LDFLAGS = \ -avoid-version -module \ diff --git a/src/plugins/litehtml_viewer/litehtml/attributes.h b/src/plugins/litehtml_viewer/litehtml/attributes.h @@ -1,35 +0,0 @@ -#ifndef LH_ATTRIBUTES_H -#define LH_ATTRIBUTES_H - -namespace litehtml -{ - struct attr_color - { - unsigned char rgbBlue; - unsigned char rgbGreen; - unsigned char rgbRed; - unsigned char rgbAlpha; - attr_color() - { - rgbAlpha = 255; - rgbBlue = 0; - rgbGreen = 0; - rgbRed = 0; - } - }; - - struct attr_border - { - style_border border; - int width; - attr_color color; - - attr_border() - { - border = borderNone; - width = 0; - } - }; -} - -#endif // LH_ATTRIBUTES_H diff --git a/src/plugins/litehtml_viewer/litehtml/background.cpp b/src/plugins/litehtml_viewer/litehtml/background.cpp @@ -1,79 +0,0 @@ -#include "html.h" -#include "background.h" - -litehtml::background::background(void) -{ - m_attachment = background_attachment_scroll; - m_repeat = background_repeat_repeat; - m_clip = background_box_border; - m_origin = background_box_padding; - m_color.alpha = 0; - m_color.red = 0; - m_color.green = 0; - m_color.blue = 0; -} - -litehtml::background::background( const background& val ) -{ - m_image = val.m_image; - m_baseurl = val.m_baseurl; - m_color = val.m_color; - m_attachment = val.m_attachment; - m_position = val.m_position; - m_repeat = val.m_repeat; - m_clip = val.m_clip; - m_origin = val.m_origin; -} - -litehtml::background::~background(void) -{ -} - -litehtml::background& litehtml::background::operator=( const background& val ) -{ - m_image = val.m_image; - m_baseurl = val.m_baseurl; - m_color = val.m_color; - m_attachment = val.m_attachment; - m_position = val.m_position; - m_repeat = val.m_repeat; - m_clip = val.m_clip; - m_origin = val.m_origin; - return *this; -} - - -litehtml::background_paint::background_paint() : color(0, 0, 0, 0) -{ - position_x = 0; - position_y = 0; - attachment = background_attachment_scroll; - repeat = background_repeat_repeat; - is_root = false; -} - -litehtml::background_paint::background_paint( const background_paint& val ) -{ - image = val.image; - baseurl = val.baseurl; - attachment = val.attachment; - repeat = val.repeat; - color = val.color; - clip_box = val.clip_box; - origin_box = val.origin_box; - border_box = val.border_box; - border_radius = val.border_radius; - image_size = val.image_size; - position_x = val.position_x; - position_y = val.position_y; - is_root = val.is_root; -} - -void litehtml::background_paint::operator=( const background& val ) -{ - attachment = val.m_attachment; - baseurl = val.m_baseurl; - image = val.m_image; - repeat = val.m_repeat; - color = val.m_color; -} diff --git a/src/plugins/litehtml_viewer/litehtml/background.h b/src/plugins/litehtml_viewer/litehtml/background.h @@ -2,7 +2,6 @@ #define LH_BACKGROUND_H #include "types.h" -#include "attributes.h" #include "css_length.h" #include "css_position.h" #include "web_color.h" @@ -13,29 +12,34 @@ namespace litehtml class background { public: - tstring m_image; - tstring m_baseurl; + string_vector m_image; + string m_baseurl; web_color m_color; - background_attachment m_attachment; - css_position m_position; - background_repeat m_repeat; - background_box m_clip; - background_box m_origin; - css_border_radius m_radius; + int_vector m_attachment; + length_vector m_position_x; + length_vector m_position_y; + size_vector m_size; + int_vector m_repeat; + int_vector m_clip; + int_vector m_origin; - public: - background(void); - background(const background& val); - ~background(void); - - background& operator=(const background& val); + bool is_empty() const + { + if(m_color.alpha != 0) return false; + if(m_image.empty()) return true; + for(const auto& img : m_image) + { + if(!img.empty()) return false; + } + return true; + } }; class background_paint { public: - tstring image; - tstring baseurl; + string image; + string baseurl; background_attachment attachment; background_repeat repeat; web_color color; @@ -47,10 +51,17 @@ namespace litehtml int position_x; int position_y; bool is_root; + public: - background_paint(); - background_paint(const background_paint& val); - void operator=(const background& val); + background_paint() + { + attachment = background_attachment_scroll; + repeat = background_repeat_repeat; + color = web_color::transparent; + position_x = 0; + position_y = 0; + is_root = false; + } }; } diff --git a/src/plugins/litehtml_viewer/litehtml/borders.h b/src/plugins/litehtml_viewer/litehtml/borders.h @@ -3,6 +3,7 @@ #include "css_length.h" #include "types.h" +#include "web_color.h" namespace litehtml { @@ -31,6 +32,8 @@ namespace litehtml color = val.color; return *this; } + + string to_string() const; }; struct border @@ -199,7 +202,7 @@ namespace litehtml bottom_right_y = val.bottom_right_y; return *this; } - border_radiuses calc_percents(int width, int height) + border_radiuses calc_percents(int width, int height) const { border_radiuses ret; ret.bottom_left_x = bottom_left_x.calc_percent(width); @@ -222,9 +225,11 @@ namespace litehtml css_border bottom; css_border_radius radius; - css_borders() - { + css_borders() = default; + bool is_visible() const + { + return left.width.val() != 0 || right.width.val() != 0 || top.width.val() != 0 || bottom.width.val() != 0; } css_borders(const css_borders& val) @@ -245,6 +250,13 @@ namespace litehtml radius = val.radius; return *this; } + string to_string() const + { + return "left: " + left.to_string() + + ", top: " + top.to_string() + + ", right: " + top.to_string() + + ", bottom: " + bottom.to_string(); + } }; struct borders @@ -255,10 +267,7 @@ namespace litehtml border bottom; border_radiuses radius; - borders() - { - - } + borders() = default; borders(const borders& val) { @@ -277,6 +286,11 @@ namespace litehtml bottom = val.bottom; } + bool is_visible() const + { + return left.width != 0 || right.width != 0 || top.width != 0 || bottom.width != 0; + } + borders& operator=(const borders& val) { left = val.left; diff --git a/src/plugins/litehtml_viewer/litehtml/box.cpp b/src/plugins/litehtml_viewer/litehtml/box.cpp @@ -1,434 +0,0 @@ -#include "html.h" -#include "box.h" -#include "html_tag.h" - - -litehtml::box_type litehtml::block_box::get_type() -{ - return box_block; -} - -int litehtml::block_box::height() -{ - return m_element->height(); -} - -int litehtml::block_box::width() -{ - return m_element->width(); -} - -void litehtml::block_box::add_element(const element::ptr &el) -{ - m_element = el; - el->m_box = this; -} - -void litehtml::block_box::finish(bool last_box) -{ - if(!m_element) return; - m_element->apply_relative_shift(m_box_right - m_box_left); -} - -bool litehtml::block_box::can_hold(const element::ptr &el, white_space ws) -{ - if(m_element || el->is_inline_box()) - { - return false; - } - return true; -} - -bool litehtml::block_box::is_empty() -{ - if(m_element) - { - return false; - } - return true; -} - -int litehtml::block_box::baseline() -{ - if(m_element) - { - return m_element->get_base_line(); - } - return 0; -} - -void litehtml::block_box::get_elements( elements_vector& els ) -{ - els.push_back(m_element); -} - -int litehtml::block_box::top_margin() -{ - if(m_element && m_element->collapse_top_margin()) - { - return m_element->m_margins.top; - } - return 0; -} - -int litehtml::block_box::bottom_margin() -{ - if(m_element && m_element->collapse_bottom_margin()) - { - return m_element->m_margins.bottom; - } - return 0; -} - -void litehtml::block_box::y_shift( int shift ) -{ - m_box_top += shift; - if(m_element) - { - m_element->m_pos.y += shift; - } -} - -void litehtml::block_box::new_width( int left, int right, elements_vector& els ) -{ - -} - -////////////////////////////////////////////////////////////////////////// - -litehtml::box_type litehtml::line_box::get_type() -{ - return box_line; -} - -int litehtml::line_box::height() -{ - return m_height; -} - -int litehtml::line_box::width() -{ - return m_width; -} - -void litehtml::line_box::add_element(const element::ptr &el) -{ - el->m_skip = false; - el->m_box = 0; - bool add = true; - if( (m_items.empty() && el->is_white_space()) || el->is_break() ) - { - el->m_skip = true; - } else if(el->is_white_space()) - { - if (have_last_space()) - { - add = false; - el->m_skip = true; - } - } - - if(add) - { - el->m_box = this; - m_items.push_back(el); - - if(!el->m_skip) - { - int el_shift_left = el->get_inline_shift_left(); - int el_shift_right = el->get_inline_shift_right(); - - el->m_pos.x = m_box_left + m_width + el_shift_left + el->content_margins_left(); - el->m_pos.y = m_box_top + el->content_margins_top(); - m_width += el->width() + el_shift_left + el_shift_right; - } - } -} - -void litehtml::line_box::finish(bool last_box) -{ - if( is_empty() || (!is_empty() && last_box && is_break_only()) ) - { - m_height = 0; - return; - } - - for(auto i = m_items.rbegin(); i != m_items.rend(); i++) - { - if((*i)->is_white_space() || (*i)->is_break()) - { - if(!(*i)->m_skip) - { - (*i)->m_skip = true; - m_width -= (*i)->width(); - } - } else - { - break; - } - } - - int base_line = m_font_metrics.base_line(); - int line_height = m_line_height; - - int add_x = 0; - switch(m_text_align) - { - case text_align_right: - if(m_width < (m_box_right - m_box_left)) - { - add_x = (m_box_right - m_box_left) - m_width; - } - break; - case text_align_center: - if(m_width < (m_box_right - m_box_left)) - { - add_x = ((m_box_right - m_box_left) - m_width) / 2; - } - break; - default: - add_x = 0; - } - - m_height = 0; - // find line box baseline and line-height - for(const auto& el : m_items) - { - if(el->get_display() == display_inline_text) - { - font_metrics fm; - el->get_font(&fm); - base_line = std::max(base_line, fm.base_line()); - line_height = std::max(line_height, el->line_height()); - m_height = std::max(m_height, fm.height); - } - el->m_pos.x += add_x; - } - - if(m_height) - { - base_line += (line_height - m_height) / 2; - } - - m_height = line_height; - - int y1 = 0; - int y2 = m_height; - - for (const auto& el : m_items) - { - if(el->get_display() == display_inline_text) - { - font_metrics fm; - el->get_font(&fm); - el->m_pos.y = m_height - base_line - fm.ascent; - } else - { - switch(el->get_vertical_align()) - { - case va_super: - case va_sub: - case va_baseline: - el->m_pos.y = m_height - base_line - el->height() + el->get_base_line() + el->content_margins_top(); - break; - case va_top: - el->m_pos.y = y1 + el->content_margins_top(); - break; - case va_text_top: - el->m_pos.y = m_height - base_line - m_font_metrics.ascent + el->content_margins_top(); - break; - case va_middle: - el->m_pos.y = m_height - base_line - m_font_metrics.x_height / 2 - el->height() / 2 + el->content_margins_top(); - break; - case va_bottom: - el->m_pos.y = y2 - el->height() + el->content_margins_top(); - break; - case va_text_bottom: - el->m_pos.y = m_height - base_line + m_font_metrics.descent - el->height() + el->content_margins_top(); - break; - } - y1 = std::min(y1, el->top()); - y2 = std::max(y2, el->bottom()); - } - } - - css_offsets offsets; - - for (const auto& el : m_items) - { - el->m_pos.y -= y1; - el->m_pos.y += m_box_top; - if(el->get_display() != display_inline_text) - { - switch(el->get_vertical_align()) - { - case va_top: - el->m_pos.y = m_box_top + el->content_margins_top(); - break; - case va_bottom: - el->m_pos.y = m_box_top + (y2 - y1) - el->height() + el->content_margins_top(); - break; - case va_baseline: - //TODO: process vertical align "baseline" - break; - case va_middle: - //TODO: process vertical align "middle" - break; - case va_sub: - //TODO: process vertical align "sub" - break; - case va_super: - //TODO: process vertical align "super" - break; - case va_text_bottom: - //TODO: process vertical align "text-bottom" - break; - case va_text_top: - //TODO: process vertical align "text-top" - break; - } - } - - el->apply_relative_shift(m_box_right - m_box_left); - } - m_height = y2 - y1; - m_baseline = (base_line - y1) - (m_height - line_height); -} - -bool litehtml::line_box::can_hold(const element::ptr &el, white_space ws) -{ - if(!el->is_inline_box()) return false; - - if(el->is_break()) - { - return false; - } - - if(ws == white_space_nowrap || ws == white_space_pre) - { - return true; - } - - if(m_box_left + m_width + el->width() + el->get_inline_shift_left() + el->get_inline_shift_right() > m_box_right) - { - return false; - } - - return true; -} - -bool litehtml::line_box::have_last_space() -{ - bool ret = false; - for (auto i = m_items.rbegin(); i != m_items.rend() && !ret; i++) - { - if((*i)->is_white_space() || (*i)->is_break()) - { - ret = true; - } else - { - break; - } - } - return ret; -} - -bool litehtml::line_box::is_empty() -{ - if(m_items.empty()) return true; - for (auto i = m_items.rbegin(); i != m_items.rend(); i++) - { - if(!(*i)->m_skip || (*i)->is_break()) - { - return false; - } - } - return true; -} - -int litehtml::line_box::baseline() -{ - return m_baseline; -} - -void litehtml::line_box::get_elements( elements_vector& els ) -{ - els.insert(els.begin(), m_items.begin(), m_items.end()); -} - -int litehtml::line_box::top_margin() -{ - return 0; -} - -int litehtml::line_box::bottom_margin() -{ - return 0; -} - -void litehtml::line_box::y_shift( int shift ) -{ - m_box_top += shift; - for (auto& el : m_items) - { - el->m_pos.y += shift; - } -} - -bool litehtml::line_box::is_break_only() -{ - if(m_items.empty()) return true; - - if(m_items.front()->is_break()) - { - for (auto& el : m_items) - { - if(!el->m_skip) - { - return false; - } - } - return true; - } - return false; -} - -void litehtml::line_box::new_width( int left, int right, elements_vector& els ) -{ - int add = left - m_box_left; - if(add) - { - m_box_left = left; - m_box_right = right; - m_width = 0; - auto remove_begin = m_items.end(); - for (auto i = m_items.begin() + 1; i != m_items.end(); i++) - { - element::ptr el = (*i); - - if(!el->m_skip) - { - if(m_box_left + m_width + el->width() + el->get_inline_shift_right() + el->get_inline_shift_left() > m_box_right) - { - remove_begin = i; - break; - } else - { - el->m_pos.x += add; - m_width += el->width() + el->get_inline_shift_right() + el->get_inline_shift_left(); - } - } - } - if(remove_begin != m_items.end()) - { - els.insert(els.begin(), remove_begin, m_items.end()); - m_items.erase(remove_begin, m_items.end()); - - for(const auto& el : els) - { - el->m_box = 0; - } - } - } -} - diff --git a/src/plugins/litehtml_viewer/litehtml/box.h b/src/plugins/litehtml_viewer/litehtml/box.h @@ -1,120 +0,0 @@ -#ifndef LH_BOX_H -#define LH_BOX_H - -namespace litehtml -{ - class html_tag; - - enum box_type - { - box_block, - box_line - }; - - class box - { - public: - typedef std::unique_ptr<litehtml::box> ptr; - typedef std::vector< box::ptr > vector; - protected: - int m_box_top; - int m_box_left; - int m_box_right; - public: - box(int top, int left, int right) - { - m_box_top = top; - m_box_left = left; - m_box_right = right; - } - virtual ~box() {} - - int bottom() { return m_box_top + height(); } - int top() { return m_box_top; } - int right() { return m_box_left + width(); } - int left() { return m_box_left; } - - virtual litehtml::box_type get_type() = 0; - virtual int height() = 0; - virtual int width() = 0; - virtual void add_element(const element::ptr &el) = 0; - virtual bool can_hold(const element::ptr &el, white_space ws) = 0; - virtual void finish(bool last_box = false) = 0; - virtual bool is_empty() = 0; - virtual int baseline() = 0; - virtual void get_elements(elements_vector& els) = 0; - virtual int top_margin() = 0; - virtual int bottom_margin() = 0; - virtual void y_shift(int shift) = 0; - virtual void new_width(int left, int right, elements_vector& els) = 0; - }; - - ////////////////////////////////////////////////////////////////////////// - - class block_box : public box - { - element::ptr m_element; - public: - block_box(int top, int left, int right) : box(top, left, right) - { - m_element = 0; - } - - virtual litehtml::box_type get_type(); - virtual int height(); - virtual int width(); - virtual void add_element(const element::ptr &el); - virtual bool can_hold(const element::ptr &el, white_space ws); - virtual void finish(bool last_box = false); - virtual bool is_empty(); - virtual int baseline(); - virtual void get_elements(elements_vector& els); - virtual int top_margin(); - virtual int bottom_margin(); - virtual void y_shift(int shift); - virtual void new_width(int left, int right, elements_vector& els); - }; - - ////////////////////////////////////////////////////////////////////////// - - class line_box : public box - { - elements_vector m_items; - int m_height; - int m_width; - int m_line_height; - font_metrics m_font_metrics; - int m_baseline; - text_align m_text_align; - public: - line_box(int top, int left, int right, int line_height, font_metrics& fm, text_align align) : box(top, left, right) - { - m_height = 0; - m_width = 0; - m_font_metrics = fm; - m_line_height = line_height; - m_baseline = 0; - m_text_align = align; - } - - virtual litehtml::box_type get_type(); - virtual int height(); - virtual int width(); - virtual void add_element(const element::ptr &el); - virtual bool can_hold(const element::ptr &el, white_space ws); - virtual void finish(bool last_box = false); - virtual bool is_empty(); - virtual int baseline(); - virtual void get_elements(elements_vector& els); - virtual int top_margin(); - virtual int bottom_margin(); - virtual void y_shift(int shift); - virtual void new_width(int left, int right, elements_vector& els); - - private: - bool have_last_space(); - bool is_break_only(); - }; -} - -#endif // LH_BOX_H diff --git a/src/plugins/litehtml_viewer/litehtml/codepoint.cpp b/src/plugins/litehtml_viewer/litehtml/codepoint.cpp @@ -0,0 +1,82 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "codepoint.h" + +#include <iostream> + +namespace { + +bool lookup(const uint32_t* table, char c) +{ + return table[c >> 5] & (1 << (c & 0x1f)); +} + +} // namespace + +namespace litehtml { + +bool is_ascii_codepoint(char c) +{ + return (c < 128); +} + +// https://datatracker.ietf.org/doc/html/rfc3986#section-2.2 +bool is_url_reserved_codepoint(char c) +{ + static const uint32_t reserved_lookup[] = { + 0x00000000, + 0xac009fda, + 0x28000001, + 0x00000000 + }; + + if (!is_ascii_codepoint(c)) { + return false; + } + return lookup(reserved_lookup, c); +} + +// https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 +bool is_url_scheme_codepoint(char c) +{ + static const uint32_t scheme_lookup[] = { + 0x00000000, + 0x03ff6800, + 0x07fffffe, + 0x07fffffe, + }; + + if (!is_ascii_codepoint(c)) { + return false; + } + return lookup(scheme_lookup, c); +} + +} // namespace litehtml diff --git a/src/plugins/litehtml_viewer/litehtml/codepoint.h b/src/plugins/litehtml_viewer/litehtml/codepoint.h @@ -0,0 +1,51 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef LITEHTML_CODEPOINT_H__ +#define LITEHTML_CODEPOINT_H__ + +#include <string> + +#include "os_types.h" + +namespace litehtml { + +bool is_ascii_codepoint(char c); + +// Returns true if the codepoint is a reserved codepoint for URLs. +// https://datatracker.ietf.org/doc/html/rfc3986#section-2.2 +bool is_url_reserved_codepoint(char c); + +// Returns true if the codepoint is a scheme codepoint for URLs. +// https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 +bool is_url_scheme_codepoint(char c); + +} // namespace litehtml + +#endif // LITEHTML_CODEPOINT_H__ diff --git a/src/plugins/litehtml_viewer/litehtml/context.cpp b/src/plugins/litehtml_viewer/litehtml/context.cpp @@ -1,12 +0,0 @@ -#include "html.h" -#include "context.h" -#include "stylesheet.h" - - -void litehtml::context::load_master_stylesheet( const tchar_t* str ) -{ - media_query_list::ptr media; - - m_master_css.parse_stylesheet(str, 0, std::shared_ptr<litehtml::document>(), media_query_list::ptr()); - m_master_css.sort_selectors(); -} diff --git a/src/plugins/litehtml_viewer/litehtml/context.h b/src/plugins/litehtml_viewer/litehtml/context.h @@ -1,20 +0,0 @@ -#ifndef LH_CONTEXT_H -#define LH_CONTEXT_H - -#include "stylesheet.h" - -namespace litehtml -{ - class context - { - litehtml::css m_master_css; - public: - void load_master_stylesheet(const tchar_t* str); - litehtml::css& master_css() - { - return m_master_css; - } - }; -} - -#endif // LH_CONTEXT_H diff --git a/src/plugins/litehtml_viewer/litehtml/css_borders.cpp b/src/plugins/litehtml_viewer/litehtml/css_borders.cpp @@ -0,0 +1,7 @@ +#include "html.h" +#include "borders.h" + +litehtml::string litehtml::css_border::to_string() const +{ + return width.to_string() + "/" + index_value(style, border_style_strings) + "/" + color.to_string(); +} diff --git a/src/plugins/litehtml_viewer/litehtml/css_length.cpp b/src/plugins/litehtml_viewer/litehtml/css_length.cpp @@ -1,17 +1,17 @@ #include "html.h" #include "css_length.h" -void litehtml::css_length::fromString( const tstring& str, const tstring& predefs, int defValue ) +void litehtml::css_length::fromString( const string& str, const string& predefs, int defValue ) { // TODO: Make support for calc - if(str.substr(0, 4) == _t("calc")) + if(str.substr(0, 4) == "calc") { m_is_predefined = true; - m_predef = 0; + m_predef = defValue; return; } - int predef = value_index(str.c_str(), predefs.c_str(), -1); + int predef = value_index(str, predefs, -1); if(predef >= 0) { m_is_predefined = true; @@ -20,16 +20,16 @@ void litehtml::css_length::fromString( const tstring& str, const tstring& predef { m_is_predefined = false; - tstring num; - tstring un; + string num; + string un; bool is_unit = false; - for(tstring::const_iterator chr = str.begin(); chr != str.end(); chr++) + for(char chr : str) { if(!is_unit) { - if(t_isdigit(*chr) || *chr == _t('.') || *chr == _t('+') || *chr == _t('-')) + if(t_isdigit(chr) || chr == '.' || chr == '+' || chr == '-') { - num += *chr; + num += chr; } else { is_unit = true; @@ -37,13 +37,13 @@ void litehtml::css_length::fromString( const tstring& str, const tstring& predef } if(is_unit) { - un += *chr; + un += chr; } } if(!num.empty()) { - m_value = (float) t_strtod(num.c_str(), 0); - m_units = (css_units) value_index(un.c_str(), css_units_strings, css_units_none); + m_value = t_strtof(num); + m_units = (css_units) value_index(un, css_units_strings, css_units_none); } else { // not a number so it is predefined @@ -52,3 +52,26 @@ void litehtml::css_length::fromString( const tstring& str, const tstring& predef } } } + +litehtml::css_length litehtml::css_length::from_string(const string& str, const string& predefs, int defValue) +{ + css_length len; + len.fromString(str, predefs, defValue); + return len; +} + +litehtml::string litehtml::css_length::to_string() const +{ + if(m_is_predefined) + { + return "def(" + std::to_string(m_predef) + ")"; + } + return std::to_string(m_value) + "{" + index_value(m_units, css_units_strings) + "}"; +} + +litehtml::css_length litehtml::css_length::predef_value(int val) +{ + css_length len; + len.predef(val); + return len; +} diff --git a/src/plugins/litehtml_viewer/litehtml/css_length.h b/src/plugins/litehtml_viewer/litehtml/css_length.h @@ -16,20 +16,24 @@ namespace litehtml bool m_is_predefined; public: css_length(); - css_length(const css_length& val); - - css_length& operator=(const css_length& val); + css_length(float val, css_units units = css_units_px); css_length& operator=(float val); + bool is_predefined() const; void predef(int val); int predef() const; + static css_length predef_value(int val = 0); void set_value(float val, css_units units); float val() const; css_units units() const; int calc_percent(int width) const; - void fromString(const tstring& str, const tstring& predefs = _t(""), int defValue = 0); + void fromString(const string& str, const string& predefs = "", int defValue = 0); + static css_length from_string(const string& str, const string& predefs = "", int defValue = 0); + string to_string() const; }; + using length_vector = std::vector<css_length>; + // css_length inlines inline css_length::css_length() @@ -40,31 +44,11 @@ namespace litehtml m_is_predefined = false; } - inline css_length::css_length(const css_length& val) + inline css_length::css_length(float val, css_units units) { - if(val.is_predefined()) - { - m_predef = val.m_predef; - } else - { - m_value = val.m_value; - } - m_units = val.m_units; - m_is_predefined = val.m_is_predefined; - } - - inline css_length& css_length::operator=(const css_length& val) - { - if(val.is_predefined()) - { - m_predef = val.m_predef; - } else - { - m_value = val.m_value; - } - m_units = val.m_units; - m_is_predefined = val.m_is_predefined; - return *this; + m_value = val; + m_units = units; + m_is_predefined = false; } inline css_length& css_length::operator=(float val) @@ -95,7 +79,7 @@ namespace litehtml return 0; } - inline void css_length::set_value(float val, css_units units) + inline void css_length::set_value(float val, css_units units) { m_value = val; m_is_predefined = false; diff --git a/src/plugins/litehtml_viewer/litehtml/css_margins.h b/src/plugins/litehtml_viewer/litehtml/css_margins.h @@ -12,10 +12,7 @@ namespace litehtml css_length top; css_length bottom; - css_margins() - { - - } + css_margins() = default; css_margins(const css_margins& val) { @@ -33,6 +30,14 @@ namespace litehtml bottom = val.bottom; return *this; } + + string to_string() const + { + return "left: " + left.to_string() + + ", right: " + right.to_string() + + ", top: " + top.to_string() + + ", bottom: " + bottom.to_string(); + } }; } diff --git a/src/plugins/litehtml_viewer/litehtml/css_offsets.h b/src/plugins/litehtml_viewer/litehtml/css_offsets.h @@ -12,10 +12,7 @@ namespace litehtml css_length right; css_length bottom; - css_offsets() - { - - } + css_offsets() = default; css_offsets(const css_offsets& val) { @@ -33,6 +30,14 @@ namespace litehtml bottom = val.bottom; return *this; } + + string to_string() const + { + return "left: " + left.to_string() + + ", top: " + top.to_string() + + ", right: " + right.to_string() + + ", bottom: " + bottom.to_string(); + } }; } diff --git a/src/plugins/litehtml_viewer/litehtml/css_position.h b/src/plugins/litehtml_viewer/litehtml/css_position.h @@ -11,29 +11,18 @@ namespace litehtml css_length y; css_length width; css_length height; + }; - css_position() - { - - } - - css_position(const css_position& val) - { - x = val.x; - y = val.y; - width = val.width; - height = val.height; - } + struct css_size + { + css_length width; + css_length height; - css_position& operator=(const css_position& val) - { - x = val.x; - y = val.y; - width = val.width; - height = val.height; - return *this; - } + css_size() = default; + css_size(css_length width, css_length height) : width(width), height(height) {} }; + + using size_vector = std::vector<css_size>; } #endif // LH_CSS_POSITION_H diff --git a/src/plugins/litehtml_viewer/litehtml/css_properties.cpp b/src/plugins/litehtml_viewer/litehtml/css_properties.cpp @@ -0,0 +1,456 @@ +#include "html.h" +#include "css_properties.h" + +#define offset(member) ((uint_ptr)&this->member - (uint_ptr)this) + +void litehtml::css_properties::compute(const element* el, const document::ptr& doc) +{ + compute_font(el, doc); + int font_size = get_font_size(); + m_color = el->get_color_property(_color_, true, web_color::black, offset(m_color)); + + m_el_position = (element_position) el->get_enum_property( _position_, false, element_position_static, offset(m_el_position)); + m_display = (style_display) el->get_enum_property( _display_, false, display_inline, offset(m_display)); + m_visibility = (visibility) el->get_enum_property( _visibility_, true, visibility_visible, offset(m_visibility)); + m_float = (element_float) el->get_enum_property( _float_, false, float_none, offset(m_float)); + m_clear = (element_clear) el->get_enum_property( _clear_, false, clear_none, offset(m_clear)); + m_box_sizing = (box_sizing) el->get_enum_property( _box_sizing_, false, box_sizing_content_box, offset(m_box_sizing)); + m_overflow = (overflow) el->get_enum_property( _overflow_, false, overflow_visible, offset(m_overflow)); + m_text_align = (text_align) el->get_enum_property( _text_align_, true, text_align_left, offset(m_text_align)); + m_vertical_align = (vertical_align) el->get_enum_property( _vertical_align_, false, va_baseline, offset(m_vertical_align)); + m_text_transform = (text_transform) el->get_enum_property( _text_transform_, true, text_transform_none, offset(m_text_transform)); + m_white_space = (white_space) el->get_enum_property( _white_space_, true, white_space_normal, offset(m_white_space)); + + // https://www.w3.org/TR/CSS22/visuren.html#dis-pos-flo + if (m_display == display_none) + { + // 1. If 'display' has the value 'none', then 'position' and 'float' do not apply. In this case, the element + // generates no box. + m_float = float_none; + } else + { + // 2. Otherwise, if 'position' has the value 'absolute' or 'fixed', the box is absolutely positioned, + // the computed value of 'float' is 'none', and display is set according to the table below. + // The position of the box will be determined by the 'top', 'right', 'bottom' and 'left' properties + // and the box's containing block. + if (m_el_position == element_position_absolute || m_el_position == element_position_fixed) + { + m_float = float_none; + + if (m_display == display_inline_table) + { + m_display = display_table; + } else if (m_display == display_inline || + m_display == display_table_row_group || + m_display == display_table_column || + m_display == display_table_column_group || + m_display == display_table_header_group || + m_display == display_table_footer_group || + m_display == display_table_row || + m_display == display_table_cell || + m_display == display_table_caption || + m_display == display_inline_block) + { + m_display = display_block; + } + } else if (m_float != float_none) + { + // 3. Otherwise, if 'float' has a value other than 'none', the box is floated and 'display' is set + // according to the table below. + if (m_display == display_inline_table) + { + m_display = display_table; + } else if (m_display == display_inline || + m_display == display_table_row_group || + m_display == display_table_column || + m_display == display_table_column_group || + m_display == display_table_header_group || + m_display == display_table_footer_group || + m_display == display_table_row || + m_display == display_table_cell || + m_display == display_table_caption || + m_display == display_inline_block) + { + m_display = display_block; + } + } else if(el->is_root()) + { + // 4. Otherwise, if the element is the root element, 'display' is set according to the table below, + // except that it is undefined in CSS 2.2 whether a specified value of 'list-item' becomes a + // computed value of 'block' or 'list-item'. + if (m_display == display_inline_table) + { + m_display = display_table; + } else if (m_display == display_inline || + m_display == display_table_row_group || + m_display == display_table_column || + m_display == display_table_column_group || + m_display == display_table_header_group || + m_display == display_table_footer_group || + m_display == display_table_row || + m_display == display_table_cell || + m_display == display_table_caption || + m_display == display_inline_block || + m_display == display_list_item) + { + m_display = display_block; + } + } + } + // 5. Otherwise, the remaining 'display' property values apply as specified. + + const css_length _auto = css_length::predef_value(0); + const css_length none = _auto, normal = _auto; + + m_css_width = el->get_length_property(_width_, false, _auto, offset(m_css_width)); + m_css_height = el->get_length_property(_height_, false, _auto, offset(m_css_height)); + + m_css_min_width = el->get_length_property(_min_width_, false, _auto, offset(m_css_min_width)); + m_css_min_height = el->get_length_property(_min_height_, false, _auto, offset(m_css_min_height)); + + m_css_max_width = el->get_length_property(_max_width_, false, none, offset(m_css_max_width)); + m_css_max_height = el->get_length_property(_max_height_, false, none, offset(m_css_max_height)); + + doc->cvt_units(m_css_width, font_size); + doc->cvt_units(m_css_height, font_size); + + doc->cvt_units(m_css_min_width, font_size); + doc->cvt_units(m_css_min_height, font_size); + + doc->cvt_units(m_css_max_width, font_size); + doc->cvt_units(m_css_max_height, font_size); + + m_css_margins.left = el->get_length_property(_margin_left_, false, 0, offset(m_css_margins.left)); + m_css_margins.right = el->get_length_property(_margin_right_, false, 0, offset(m_css_margins.right)); + m_css_margins.top = el->get_length_property(_margin_top_, false, 0, offset(m_css_margins.top)); + m_css_margins.bottom = el->get_length_property(_margin_bottom_, false, 0, offset(m_css_margins.bottom)); + + doc->cvt_units(m_css_margins.left, font_size); + doc->cvt_units(m_css_margins.right, font_size); + doc->cvt_units(m_css_margins.top, font_size); + doc->cvt_units(m_css_margins.bottom, font_size); + + m_css_padding.left = el->get_length_property(_padding_left_, false, 0, offset(m_css_padding.left)); + m_css_padding.right = el->get_length_property(_padding_right_, false, 0, offset(m_css_padding.right)); + m_css_padding.top = el->get_length_property(_padding_top_, false, 0, offset(m_css_padding.top)); + m_css_padding.bottom = el->get_length_property(_padding_bottom_, false, 0, offset(m_css_padding.bottom)); + + doc->cvt_units(m_css_padding.left, font_size); + doc->cvt_units(m_css_padding.right, font_size); + doc->cvt_units(m_css_padding.top, font_size); + doc->cvt_units(m_css_padding.bottom, font_size); + + m_css_borders.left.color = el->get_color_property(_border_left_color_, false, m_color, offset(m_css_borders.left.color)); + m_css_borders.right.color = el->get_color_property(_border_right_color_, false, m_color, offset(m_css_borders.right.color)); + m_css_borders.top.color = el->get_color_property(_border_top_color_, false, m_color, offset(m_css_borders.top.color)); + m_css_borders.bottom.color = el->get_color_property(_border_bottom_color_, false, m_color, offset(m_css_borders.bottom.color)); + + m_css_borders.left.style = (border_style) el->get_enum_property(_border_left_style_, false, border_style_none, offset(m_css_borders.left.style)); + m_css_borders.right.style = (border_style) el->get_enum_property(_border_right_style_, false, border_style_none, offset(m_css_borders.right.style)); + m_css_borders.top.style = (border_style) el->get_enum_property(_border_top_style_, false, border_style_none, offset(m_css_borders.top.style)); + m_css_borders.bottom.style = (border_style) el->get_enum_property(_border_bottom_style_, false, border_style_none, offset(m_css_borders.bottom.style)); + + m_css_borders.left.width = el->get_length_property(_border_left_width_, false, border_width_medium_value, offset(m_css_borders.left.width)); + m_css_borders.right.width = el->get_length_property(_border_right_width_, false, border_width_medium_value, offset(m_css_borders.right.width)); + m_css_borders.top.width = el->get_length_property(_border_top_width_, false, border_width_medium_value, offset(m_css_borders.top.width)); + m_css_borders.bottom.width = el->get_length_property(_border_bottom_width_, false, border_width_medium_value, offset(m_css_borders.bottom.width)); + + if (m_css_borders.left.style == border_style_none || m_css_borders.left.style == border_style_hidden) + m_css_borders.left.width = 0; + if (m_css_borders.right.style == border_style_none || m_css_borders.right.style == border_style_hidden) + m_css_borders.right.width = 0; + if (m_css_borders.top.style == border_style_none || m_css_borders.top.style == border_style_hidden) + m_css_borders.top.width = 0; + if (m_css_borders.bottom.style == border_style_none || m_css_borders.bottom.style == border_style_hidden) + m_css_borders.bottom.width = 0; + + doc->cvt_units(m_css_borders.left.width, font_size); + doc->cvt_units(m_css_borders.right.width, font_size); + doc->cvt_units(m_css_borders.top.width, font_size); + doc->cvt_units(m_css_borders.bottom.width, font_size); + + m_css_borders.radius.top_left_x = el->get_length_property(_border_top_left_radius_x_, false, 0, offset(m_css_borders.radius.top_left_x)); + m_css_borders.radius.top_left_y = el->get_length_property(_border_top_left_radius_y_, false, 0, offset(m_css_borders.radius.top_left_y)); + + m_css_borders.radius.top_right_x = el->get_length_property(_border_top_right_radius_x_, false, 0, offset(m_css_borders.radius.top_right_x)); + m_css_borders.radius.top_right_y = el->get_length_property(_border_top_right_radius_y_, false, 0, offset(m_css_borders.radius.top_right_y)); + + m_css_borders.radius.bottom_left_x = el->get_length_property(_border_bottom_left_radius_x_, false, 0, offset(m_css_borders.radius.bottom_left_x)); + m_css_borders.radius.bottom_left_y = el->get_length_property(_border_bottom_left_radius_y_, false, 0, offset(m_css_borders.radius.bottom_left_y)); + + m_css_borders.radius.bottom_right_x = el->get_length_property(_border_bottom_right_radius_x_, false, 0, offset(m_css_borders.radius.bottom_right_x)); + m_css_borders.radius.bottom_right_y = el->get_length_property(_border_bottom_right_radius_y_, false, 0, offset(m_css_borders.radius.bottom_right_y)); + + doc->cvt_units( m_css_borders.radius.top_left_x, font_size); + doc->cvt_units( m_css_borders.radius.top_left_y, font_size); + doc->cvt_units( m_css_borders.radius.top_right_x, font_size); + doc->cvt_units( m_css_borders.radius.top_right_y, font_size); + doc->cvt_units( m_css_borders.radius.bottom_left_x, font_size); + doc->cvt_units( m_css_borders.radius.bottom_left_y, font_size); + doc->cvt_units( m_css_borders.radius.bottom_right_x, font_size); + doc->cvt_units( m_css_borders.radius.bottom_right_y, font_size); + + m_border_collapse = (border_collapse) el->get_enum_property(_border_collapse_, true, border_collapse_separate, offset(m_border_collapse)); + + m_css_border_spacing_x = el->get_length_property(__litehtml_border_spacing_x_, true, 0, offset(m_css_border_spacing_x)); + m_css_border_spacing_y = el->get_length_property(__litehtml_border_spacing_y_, true, 0, offset(m_css_border_spacing_y)); + + doc->cvt_units(m_css_border_spacing_x, font_size); + doc->cvt_units(m_css_border_spacing_y, font_size); + + m_css_offsets.left = el->get_length_property(_left_, false, _auto, offset(m_css_offsets.left)); + m_css_offsets.right = el->get_length_property(_right_, false, _auto, offset(m_css_offsets.right)); + m_css_offsets.top = el->get_length_property(_top_, false, _auto, offset(m_css_offsets.top)); + m_css_offsets.bottom = el->get_length_property(_bottom_,false, _auto, offset(m_css_offsets.bottom)); + + doc->cvt_units(m_css_offsets.left, font_size); + doc->cvt_units(m_css_offsets.right, font_size); + doc->cvt_units(m_css_offsets.top, font_size); + doc->cvt_units(m_css_offsets.bottom, font_size); + + m_z_index = el->get_length_property(_z_index_, false, _auto, offset(m_z_index)); + m_content = el->get_string_property(_content_, false, "", offset(m_content)); + m_cursor = el->get_string_property(_cursor_, true, "auto", offset(m_cursor)); + + m_css_text_indent = el->get_length_property(_text_indent_, true, 0, offset(m_css_text_indent)); + doc->cvt_units(m_css_text_indent, font_size); + + m_css_line_height = el->get_length_property(_line_height_, true, normal, offset(m_css_line_height)); + if(m_css_line_height.is_predefined()) + { + m_line_height = m_font_metrics.height; + } else if(m_css_line_height.units() == css_units_none) + { + m_line_height = (int) (m_css_line_height.val() * font_size); + } else + { + m_line_height = doc->to_pixels(m_css_line_height, font_size, font_size); + m_css_line_height = (float) m_line_height; + } + + m_list_style_type = (list_style_type) el->get_enum_property(_list_style_type_, true, list_style_type_disc, offset(m_list_style_type)); + m_list_style_position = (list_style_position) el->get_enum_property(_list_style_position_, true, list_style_position_outside, offset(m_list_style_position)); + + m_list_style_image = el->get_string_property(_list_style_image_, true, "", offset(m_list_style_image)); + if (!m_list_style_image.empty()) + { + m_list_style_image_baseurl = el->get_string_property(_list_style_image_baseurl_, true, "", offset(m_list_style_image_baseurl)); + doc->container()->load_image(m_list_style_image.c_str(), m_list_style_image_baseurl.c_str(), true); + } + + compute_background(el, doc); + compute_flex(el, doc); +} + +static const int font_size_table[8][7] = +{ + { 9, 9, 9, 9, 11, 14, 18}, + { 9, 9, 9, 10, 12, 15, 20}, + { 9, 9, 9, 11, 13, 17, 22}, + { 9, 9, 10, 12, 14, 18, 24}, + { 9, 9, 10, 13, 16, 20, 26}, + { 9, 9, 11, 14, 17, 21, 28}, + { 9, 10, 12, 15, 17, 23, 30}, + { 9, 10, 13, 16, 18, 24, 32} +}; + +void litehtml::css_properties::compute_font(const element* el, const document::ptr& doc) +{ + // initialize font size + css_length sz = el->get_length_property(_font_size_, true, css_length::predef_value(font_size_medium), offset(m_font_size)); + + int parent_sz = 0; + int doc_font_size = doc->container()->get_default_font_size(); + element::ptr el_parent = el->parent(); + if (el_parent) + { + parent_sz = el_parent->css().get_font_size(); + } else + { + parent_sz = doc_font_size; + } + + int font_size = parent_sz; + + if(sz.is_predefined()) + { + int idx_in_table = doc_font_size - 9; + if(idx_in_table >= 0 && idx_in_table <= 7) + { + if(sz.predef() >= font_size_xx_small && sz.predef() <= font_size_xx_large) + { + font_size = font_size_table[idx_in_table][sz.predef()]; + } else if(sz.predef() == font_size_smaller) + { + font_size = (int) (parent_sz / 1.2); + } else if(sz.predef() == font_size_larger) + { + font_size = (int) (parent_sz * 1.2); + } else + { + font_size = parent_sz; + } + } else + { + switch(sz.predef()) + { + case font_size_xx_small: + font_size = doc_font_size * 3 / 5; + break; + case font_size_x_small: + font_size = doc_font_size * 3 / 4; + break; + case font_size_small: + font_size = doc_font_size * 8 / 9; + break; + case font_size_large: + font_size = doc_font_size * 6 / 5; + break; + case font_size_x_large: + font_size = doc_font_size * 3 / 2; + break; + case font_size_xx_large: + font_size = doc_font_size * 2; + break; + case font_size_smaller: + font_size = (int) (parent_sz / 1.2); + break; + case font_size_larger: + font_size = (int) (parent_sz * 1.2); + break; + default: + font_size = parent_sz; + break; + } + } + } else + { + if(sz.units() == css_units_percentage) + { + font_size = sz.calc_percent(parent_sz); + } else + { + font_size = doc->to_pixels(sz, parent_sz); + } + } + + m_font_size = (float)font_size; + + // initialize font + m_font_family = el->get_string_property(_font_family_, true, doc->container()->get_default_font_name(), offset(m_font_family)); + m_font_weight = (font_weight) el->get_enum_property( _font_weight_, true, font_weight_normal, offset(m_font_weight)); + m_font_style = (font_style) el->get_enum_property( _font_style_, true, font_style_normal, offset(m_font_style)); + m_text_decoration = el->get_string_property(_text_decoration_, true, "none", offset(m_text_decoration)); + + m_font = doc->get_font( + m_font_family.c_str(), + font_size, + index_value(m_font_weight, font_weight_strings).c_str(), + index_value(m_font_style, font_style_strings).c_str(), + m_text_decoration.c_str(), + &m_font_metrics); +} + +void litehtml::css_properties::compute_background(const element* el, const document::ptr& doc) +{ + int font_size = get_font_size(); + + m_bg.m_color = el->get_color_property(_background_color_, false, web_color::transparent, offset(m_bg.m_color)); + + const css_size auto_auto(css_length::predef_value(background_size_auto), css_length::predef_value(background_size_auto)); + m_bg.m_position_x = el->get_length_vector_property(_background_position_x_, false, { css_length(0, css_units_percentage) }, offset(m_bg.m_position_x)); + m_bg.m_position_y = el->get_length_vector_property(_background_position_y_, false, { css_length(0, css_units_percentage) }, offset(m_bg.m_position_y)); + m_bg.m_size = el->get_size_vector_property (_background_size_, false, { auto_auto }, offset(m_bg.m_size)); + + for (auto& x : m_bg.m_position_x) doc->cvt_units(x, font_size); + for (auto& y : m_bg.m_position_y) doc->cvt_units(y, font_size); + for (auto& size : m_bg.m_size) + { + doc->cvt_units(size.width, font_size); + doc->cvt_units(size.height, font_size); + } + + m_bg.m_attachment = el->get_int_vector_property(_background_attachment_, false, { background_attachment_scroll }, offset(m_bg.m_attachment)); + m_bg.m_repeat = el->get_int_vector_property(_background_repeat_, false, { background_repeat_repeat }, offset(m_bg.m_repeat)); + m_bg.m_clip = el->get_int_vector_property(_background_clip_, false, { background_box_border }, offset(m_bg.m_clip)); + m_bg.m_origin = el->get_int_vector_property(_background_origin_, false, { background_box_padding }, offset(m_bg.m_origin)); + + m_bg.m_image = el->get_string_vector_property(_background_image_, false, {""}, offset(m_bg.m_image)); + m_bg.m_baseurl = el->get_string_property(_background_image_baseurl_, false, "", offset(m_bg.m_baseurl)); + + for (const auto& image : m_bg.m_image) + { + if (!image.empty()) + { + doc->container()->load_image(image.c_str(), m_bg.m_baseurl.c_str(), true); + } + } +} + +void litehtml::css_properties::compute_flex(const element* el, const document::ptr& doc) +{ + if (m_display == display_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_content = (flex_align_content) el->get_enum_property(_align_content_, false, flex_align_content_stretch, offset(m_flex_align_content)); + } + auto parent = el->parent(); + if (parent && parent->css().m_display == display_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)); + doc->cvt_units(m_flex_basis, get_font_size()); + if(m_display == display_inline || m_display == display_inline_block) + { + m_display = display_block; + } else if(m_display == display_inline_table) + { + m_display = display_table; + } else if(m_display == display_inline_flex) + { + m_display = display_flex; + } + } +} + +std::vector<std::tuple<litehtml::string, litehtml::string>> litehtml::css_properties::dump_get_attrs() +{ + std::vector<std::tuple<string, string>> ret; + + ret.emplace_back(std::make_tuple("display", index_value(m_display, style_display_strings))); + ret.emplace_back(std::make_tuple("el_position", index_value(m_el_position, element_position_strings))); + ret.emplace_back(std::make_tuple("text_align", index_value(m_text_align, text_align_strings))); + ret.emplace_back(std::make_tuple("font_size", m_font_size.to_string())); + ret.emplace_back(std::make_tuple("overflow", index_value(m_overflow, overflow_strings))); + ret.emplace_back(std::make_tuple("white_space", index_value(m_white_space, white_space_strings))); + ret.emplace_back(std::make_tuple("visibility", index_value(m_visibility, visibility_strings))); + ret.emplace_back(std::make_tuple("box_sizing", index_value(m_box_sizing, box_sizing_strings))); + ret.emplace_back(std::make_tuple("z_index", m_z_index.to_string())); + ret.emplace_back(std::make_tuple("vertical_align", index_value(m_vertical_align, vertical_align_strings))); + ret.emplace_back(std::make_tuple("float", index_value(m_float, element_float_strings))); + ret.emplace_back(std::make_tuple("clear", index_value(m_clear, element_clear_strings))); + ret.emplace_back(std::make_tuple("margins", m_css_margins.to_string())); + ret.emplace_back(std::make_tuple("padding", m_css_padding.to_string())); + ret.emplace_back(std::make_tuple("borders", m_css_borders.to_string())); + ret.emplace_back(std::make_tuple("width", m_css_width.to_string())); + ret.emplace_back(std::make_tuple("height", m_css_height.to_string())); + ret.emplace_back(std::make_tuple("min_width", m_css_min_width.to_string())); + ret.emplace_back(std::make_tuple("min_height", m_css_min_width.to_string())); + ret.emplace_back(std::make_tuple("max_width", m_css_max_width.to_string())); + ret.emplace_back(std::make_tuple("max_height", m_css_max_width.to_string())); + ret.emplace_back(std::make_tuple("offsets", m_css_offsets.to_string())); + ret.emplace_back(std::make_tuple("text_indent", m_css_text_indent.to_string())); + ret.emplace_back(std::make_tuple("line_height", std::to_string(m_line_height))); + ret.emplace_back(std::make_tuple("list_style_type", index_value(m_list_style_type, list_style_type_strings))); + ret.emplace_back(std::make_tuple("list_style_position", index_value(m_list_style_position, list_style_position_strings))); + ret.emplace_back(std::make_tuple("border_spacing_x", m_css_border_spacing_x.to_string())); + ret.emplace_back(std::make_tuple("border_spacing_y", m_css_border_spacing_y.to_string())); + + return ret; +} diff --git a/src/plugins/litehtml_viewer/litehtml/css_properties.h b/src/plugins/litehtml_viewer/litehtml/css_properties.h @@ -0,0 +1,643 @@ +#ifndef LITEHTML_CSS_PROPERTIES_H +#define LITEHTML_CSS_PROPERTIES_H + +#include "os_types.h" +#include "types.h" +#include "css_margins.h" +#include "borders.h" +#include "css_offsets.h" +#include "background.h" + +namespace litehtml +{ + class element; + class document; + + class css_properties + { + private: + element_position m_el_position; + text_align m_text_align; + overflow m_overflow; + white_space m_white_space; + style_display m_display; + visibility m_visibility; + box_sizing m_box_sizing; + css_length m_z_index; + vertical_align m_vertical_align; + element_float m_float; + element_clear m_clear; + css_margins m_css_margins; + css_margins m_css_padding; + css_borders m_css_borders; + css_length m_css_width; + css_length m_css_height; + css_length m_css_min_width; + css_length m_css_min_height; + css_length m_css_max_width; + css_length m_css_max_height; + css_offsets m_css_offsets; + css_length m_css_text_indent; + css_length m_css_line_height; + int m_line_height; + list_style_type m_list_style_type; + list_style_position m_list_style_position; + string m_list_style_image; + string m_list_style_image_baseurl; + background m_bg; + uint_ptr m_font; + css_length m_font_size; + string m_font_family; + font_weight m_font_weight; + font_style m_font_style; + string m_text_decoration; + font_metrics m_font_metrics; + text_transform m_text_transform; + web_color m_color; + string m_cursor; + string m_content; + border_collapse m_border_collapse; + css_length m_css_border_spacing_x; + css_length m_css_border_spacing_y; + + float m_flex_grow; + float m_flex_shrink; + css_length m_flex_basis; + flex_direction m_flex_direction; + 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_content m_flex_align_content; + + 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); + void compute_flex(const element* el, const std::shared_ptr<document>& doc); + + public: + css_properties() : + m_el_position(element_position_static), + m_text_align(text_align_left), + m_overflow(overflow_visible), + m_white_space(white_space_normal), + m_display(display_inline), + m_visibility(visibility_visible), + m_box_sizing(box_sizing_content_box), + m_z_index(0), + m_vertical_align(va_baseline), + m_float(float_none), + m_clear(clear_none), + m_css_margins(), + m_css_padding(), + m_css_borders(), + m_css_width(), + m_css_height(), + m_css_min_width(), + m_css_min_height(), + m_css_max_width(), + m_css_max_height(), + m_css_offsets(), + m_css_text_indent(), + m_css_line_height(0), + m_line_height(0), + m_list_style_type(list_style_type_none), + m_list_style_position(list_style_position_outside), + m_bg(), + m_font_size(0), + m_font(0), + m_font_metrics(), + m_text_transform(text_transform_none), + m_border_collapse(border_collapse_separate), + m_css_border_spacing_x(), + m_css_border_spacing_y(), + m_flex_grow(0), + m_flex_shrink(1), + m_flex_direction(flex_direction_row), + 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) + {} + + void compute(const element* el, const std::shared_ptr<document>& doc); + std::vector<std::tuple<string, string>> dump_get_attrs(); + + element_position get_position() const; + void set_position(element_position mElPosition); + + text_align get_text_align() const; + void set_text_align(text_align mTextAlign); + + overflow get_overflow() const; + void set_overflow(overflow mOverflow); + + white_space get_white_space() const; + void set_white_space(white_space mWhiteSpace); + + style_display get_display() const; + void set_display(style_display mDisplay); + + visibility get_visibility() const; + void set_visibility(visibility mVisibility); + + box_sizing get_box_sizing() const; + void set_box_sizing(box_sizing mBoxSizing); + + int get_z_index() const; + void set_z_index(int mZIndex); + + vertical_align get_vertical_align() const; + void set_vertical_align(vertical_align mVerticalAlign); + + element_float get_float() const; + void set_float(element_float mFloat); + + element_clear get_clear() const; + void set_clear(element_clear mClear); + + const css_margins &get_margins() const; + void set_margins(const css_margins &mCssMargins); + + const css_margins &get_padding() const; + void set_padding(const css_margins &mCssPadding); + + const css_borders &get_borders() const; + void set_borders(const css_borders &mCssBorders); + + const css_length &get_width() const; + void set_width(const css_length &mCssWidth); + + const css_length &get_height() const; + void set_height(const css_length &mCssHeight); + + const css_length &get_min_width() const; + void set_min_width(const css_length &mCssMinWidth); + + const css_length &get_min_height() const; + void set_min_height(const css_length &mCssMinHeight); + + const css_length &get_max_width() const; + void set_max_width(const css_length &mCssMaxWidth); + + const css_length &get_max_height() const; + void set_max_height(const css_length &mCssMaxHeight); + + const css_offsets &get_offsets() const; + void set_offsets(const css_offsets &mCssOffsets); + + const css_length &get_text_indent() const; + void set_text_indent(const css_length &mCssTextIndent); + + int get_line_height() const; + void set_line_height(int mLineHeight); + + list_style_type get_list_style_type() const; + void set_list_style_type(list_style_type mListStyleType); + + list_style_position get_list_style_position() const; + void set_list_style_position(list_style_position mListStylePosition); + + string get_list_style_image() const; + void set_list_style_image(const string& url); + + string get_list_style_image_baseurl() const; + void set_list_style_image_baseurl(const string& url); + + const background &get_bg() const; + void set_bg(const background &mBg); + + int get_font_size() const; + void set_font_size(int mFontSize); + + uint_ptr get_font() const; + void set_font(uint_ptr mFont); + + const font_metrics& get_font_metrics() const; + void set_font_metrics(const font_metrics& mFontMetrics); + + text_transform get_text_transform() const; + void set_text_transform(text_transform mTextTransform); + + web_color get_color() const; + void set_color(web_color color); + + string get_cursor() const; + void set_cursor(const string& cursor); + + string get_content() const; + void set_content(const string& content); + + border_collapse get_border_collapse() const; + void set_border_collapse(border_collapse mBorderCollapse); + + const css_length& get_border_spacing_x() const ; + void set_border_spacing_x(const css_length& mBorderSpacingX); + + const css_length& get_border_spacing_y() const; + void set_border_spacing_y(const css_length& mBorderSpacingY); + + float get_flex_grow() const; + float get_flex_shrink() const; + const css_length& get_flex_basis() const; + flex_direction get_flex_direction() const; + 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_content get_flex_align_content() const; + }; + + inline element_position css_properties::get_position() const + { + return m_el_position; + } + + inline void css_properties::set_position(element_position mElPosition) + { + m_el_position = mElPosition; + } + + inline text_align css_properties::get_text_align() const + { + return m_text_align; + } + + inline void css_properties::set_text_align(text_align mTextAlign) + { + m_text_align = mTextAlign; + } + + inline overflow css_properties::get_overflow() const + { + return m_overflow; + } + + inline void css_properties::set_overflow(overflow mOverflow) + { + m_overflow = mOverflow; + } + + inline white_space css_properties::get_white_space() const + { + return m_white_space; + } + + inline void css_properties::set_white_space(white_space mWhiteSpace) + { + m_white_space = mWhiteSpace; + } + + inline style_display css_properties::get_display() const + { + return m_display; + } + + inline void css_properties::set_display(style_display mDisplay) + { + m_display = mDisplay; + } + + inline visibility css_properties::get_visibility() const + { + return m_visibility; + } + + inline void css_properties::set_visibility(visibility mVisibility) + { + m_visibility = mVisibility; + } + + inline box_sizing css_properties::get_box_sizing() const + { + return m_box_sizing; + } + + inline void css_properties::set_box_sizing(box_sizing mBoxSizing) + { + m_box_sizing = mBoxSizing; + } + + inline int css_properties::get_z_index() const + { + return (int)m_z_index.val(); + } + + inline void css_properties::set_z_index(int mZIndex) + { + m_z_index.set_value((float)mZIndex, css_units_none); + } + + inline vertical_align css_properties::get_vertical_align() const + { + return m_vertical_align; + } + + inline void css_properties::set_vertical_align(vertical_align mVerticalAlign) + { + m_vertical_align = mVerticalAlign; + } + + inline element_float css_properties::get_float() const + { + return m_float; + } + + inline void css_properties::set_float(element_float mFloat) + { + m_float = mFloat; + } + + inline element_clear css_properties::get_clear() const + { + return m_clear; + } + + inline void css_properties::set_clear(element_clear mClear) + { + m_clear = mClear; + } + + inline const css_margins &css_properties::get_margins() const + { + return m_css_margins; + } + + inline void css_properties::set_margins(const css_margins &mCssMargins) + { + m_css_margins = mCssMargins; + } + + inline const css_margins &css_properties::get_padding() const + { + return m_css_padding; + } + + inline void css_properties::set_padding(const css_margins &mCssPadding) + { + m_css_padding = mCssPadding; + } + + inline const css_borders &css_properties::get_borders() const + { + return m_css_borders; + } + + inline void css_properties::set_borders(const css_borders &mCssBorders) + { + m_css_borders = mCssBorders; + } + + inline const css_length &css_properties::get_width() const + { + return m_css_width; + } + + inline void css_properties::set_width(const css_length &mCssWidth) + { + m_css_width = mCssWidth; + } + + inline const css_length &css_properties::get_height() const + { + return m_css_height; + } + + inline void css_properties::set_height(const css_length &mCssHeight) + { + m_css_height = mCssHeight; + } + + inline const css_length &css_properties::get_min_width() const + { + return m_css_min_width; + } + + inline void css_properties::set_min_width(const css_length &mCssMinWidth) + { + m_css_min_width = mCssMinWidth; + } + + inline const css_length &css_properties::get_min_height() const + { + return m_css_min_height; + } + + inline void css_properties::set_min_height(const css_length &mCssMinHeight) + { + m_css_min_height = mCssMinHeight; + } + + inline const css_length &css_properties::get_max_width() const + { + return m_css_max_width; + } + + inline void css_properties::set_max_width(const css_length &mCssMaxWidth) + { + m_css_max_width = mCssMaxWidth; + } + + inline const css_length &css_properties::get_max_height() const + { + return m_css_max_height; + } + + inline void css_properties::set_max_height(const css_length &mCssMaxHeight) + { + m_css_max_height = mCssMaxHeight; + } + + inline const css_offsets &css_properties::get_offsets() const + { + return m_css_offsets; + } + + inline void css_properties::set_offsets(const css_offsets &mCssOffsets) + { + m_css_offsets = mCssOffsets; + } + + inline const css_length &css_properties::get_text_indent() const + { + return m_css_text_indent; + } + + inline void css_properties::set_text_indent(const css_length &mCssTextIndent) + { + m_css_text_indent = mCssTextIndent; + } + + inline int css_properties::get_line_height() const + { + return m_line_height; + } + + inline void css_properties::set_line_height(int mLineHeight) + { + m_line_height = mLineHeight; + } + + inline list_style_type css_properties::get_list_style_type() const + { + return m_list_style_type; + } + + inline void css_properties::set_list_style_type(list_style_type mListStyleType) + { + m_list_style_type = mListStyleType; + } + + inline list_style_position css_properties::get_list_style_position() const + { + return m_list_style_position; + } + + inline void css_properties::set_list_style_position(list_style_position mListStylePosition) + { + m_list_style_position = mListStylePosition; + } + + inline string css_properties::get_list_style_image() const { return m_list_style_image; } + inline void css_properties::set_list_style_image(const string& url) { m_list_style_image = url; } + + inline string css_properties::get_list_style_image_baseurl() const { return m_list_style_image_baseurl; } + inline void css_properties::set_list_style_image_baseurl(const string& url) { m_list_style_image_baseurl = url; } + + inline const background &css_properties::get_bg() const + { + return m_bg; + } + + inline void css_properties::set_bg(const background &mBg) + { + m_bg = mBg; + } + + inline int css_properties::get_font_size() const + { + return (int)m_font_size.val(); + } + + inline void css_properties::set_font_size(int mFontSize) + { + m_font_size = (float)mFontSize; + } + + inline uint_ptr css_properties::get_font() const + { + return m_font; + } + + inline void css_properties::set_font(uint_ptr mFont) + { + m_font = mFont; + } + + inline const font_metrics& css_properties::get_font_metrics() const + { + return m_font_metrics; + } + + inline void css_properties::set_font_metrics(const font_metrics& mFontMetrics) + { + m_font_metrics = mFontMetrics; + } + + inline text_transform css_properties::get_text_transform() const + { + return m_text_transform; + } + + inline void css_properties::set_text_transform(text_transform mTextTransform) + { + m_text_transform = mTextTransform; + } + + inline web_color css_properties::get_color() const { return m_color; } + inline void css_properties::set_color(web_color color) { m_color = color; } + + inline string css_properties::get_cursor() const { return m_cursor; } + inline void css_properties::set_cursor(const string& cursor) { m_cursor = cursor; } + + inline string css_properties::get_content() const { return m_content; } + inline void css_properties::set_content(const string& content) { m_content = content; } + + inline border_collapse css_properties::get_border_collapse() const + { + return m_border_collapse; + } + + inline void css_properties::set_border_collapse(border_collapse mBorderCollapse) + { + m_border_collapse = mBorderCollapse; + } + + inline const css_length& css_properties::get_border_spacing_x() const + { + return m_css_border_spacing_x; + } + + inline void css_properties::set_border_spacing_x(const css_length& mBorderSpacingX) + { + m_css_border_spacing_x = mBorderSpacingX; + } + + inline const css_length& css_properties::get_border_spacing_y() const + { + return m_css_border_spacing_y; + } + + inline void css_properties::set_border_spacing_y(const css_length& mBorderSpacingY) + { + m_css_border_spacing_y = mBorderSpacingY; + } + + inline float css_properties::get_flex_grow() const + { + return m_flex_grow; + } + + inline float css_properties::get_flex_shrink() const + { + return m_flex_shrink; + } + + inline const css_length& css_properties::get_flex_basis() const + { + return m_flex_basis; + } + + inline flex_direction css_properties::get_flex_direction() const + { + return m_flex_direction; + } + + inline flex_wrap css_properties::get_flex_wrap() const + { + return m_flex_wrap; + } + + inline flex_justify_content css_properties::get_flex_justify_content() const + { + return m_flex_justify_content; + } + + inline flex_align_items css_properties::get_flex_align_items() const + { + return m_flex_align_items; + } + + inline flex_align_self css_properties::get_flex_align_self() const + { + return m_flex_align_self; + } + + inline flex_align_content css_properties::get_flex_align_content() const + { + return m_flex_align_content; + } +} + +#endif //LITEHTML_CSS_PROPERTIES_H diff --git a/src/plugins/litehtml_viewer/litehtml/css_selector.cpp b/src/plugins/litehtml_viewer/litehtml/css_selector.cpp @@ -2,190 +2,245 @@ #include "css_selector.h" #include "document.h" -void litehtml::css_element_selector::parse( const tstring& txt ) +void litehtml::css_element_selector::parse_nth_child_params(const string& param, int& num, int& off) { - tstring::size_type el_end = txt.find_first_of(_t(".#[:")); - m_tag = txt.substr(0, el_end); - litehtml::lcase(m_tag); + if (param == "odd") + { + num = 2; + off = 1; + } + else if (param == "even") + { + num = 2; + off = 0; + } + else + { + string_vector tokens; + split_string(param, tokens, " n", "n"); + + string s_num; + string s_off; + + string s_int; + for (const auto& token : tokens) + { + if (token == "n") + { + s_num = s_int; + s_int.clear(); + } + else + { + s_int += token; + } + } + s_off = s_int; + + num = atoi(s_num.c_str()); + off = atoi(s_off.c_str()); + } +} + +void litehtml::css_element_selector::parse( const string& txt ) +{ + string::size_type el_end = txt.find_first_of(".#[:"); + string tag = txt.substr(0, el_end); + litehtml::lcase(tag); + if (tag == "") tag = "*"; + m_tag = _id(tag); + m_attrs.clear(); - while(el_end != tstring::npos) + while(el_end != string::npos) { - if(txt[el_end] == _t('.')) + if(txt[el_end] == '.') + { + css_attribute_selector attribute; + + attribute.type = select_class; + string::size_type pos = txt.find_first_of(".#[:", el_end + 1); + string name = txt.substr(el_end + 1, pos - el_end - 1); + litehtml::lcase(name); + attribute.name = _id(name); + m_attrs.push_back(attribute); + el_end = pos; + } else if(txt[el_end] == '#') { css_attribute_selector attribute; - tstring::size_type pos = txt.find_first_of(_t(".#[:"), el_end + 1); - attribute.val = txt.substr(el_end + 1, pos - el_end - 1); - split_string( attribute.val, attribute.class_val, _t(" ") ); - attribute.condition = select_equal; - attribute.attribute = _t("class"); + attribute.type = select_id; + string::size_type pos = txt.find_first_of(".#[:", el_end + 1); + string name = txt.substr(el_end + 1, pos - el_end - 1); + litehtml::lcase(name); + attribute.name = _id(name); m_attrs.push_back(attribute); el_end = pos; - } else if(txt[el_end] == _t(':')) + } else if(txt[el_end] == ':') { css_attribute_selector attribute; - if(txt[el_end + 1] == _t(':')) + if(txt[el_end + 1] == ':') { - tstring::size_type pos = txt.find_first_of(_t(".#[:"), el_end + 2); - attribute.val = txt.substr(el_end + 2, pos - el_end - 2); - attribute.condition = select_pseudo_element; - litehtml::lcase(attribute.val); - attribute.attribute = _t("pseudo-el"); + attribute.type = select_pseudo_element; + string::size_type pos = txt.find_first_of(".#[:", el_end + 2); + string name = txt.substr(el_end + 2, pos - el_end - 2); + litehtml::lcase(name); + attribute.name = _id(name); m_attrs.push_back(attribute); el_end = pos; } else { - tstring::size_type pos = txt.find_first_of(_t(".#[:("), el_end + 1); - if(pos != tstring::npos && txt.at(pos) == _t('(')) + string::size_type pos = txt.find_first_of(".#[:(", el_end + 1); + string name = txt.substr(el_end + 1, pos - el_end - 1); + lcase(name); + attribute.name = _id(name); + if(attribute.name == _after_ || attribute.name == _before_) { - pos = find_close_bracket(txt, pos); - if(pos != tstring::npos) - { - pos++; - } else - { - int iii = 0; - iii++; - } - } - if(pos != tstring::npos) - { - attribute.val = txt.substr(el_end + 1, pos - el_end - 1); + attribute.type = select_pseudo_element; } else { - attribute.val = txt.substr(el_end + 1); + attribute.type = select_pseudo_class; } - litehtml::lcase(attribute.val); - if(attribute.val == _t("after") || attribute.val == _t("before")) + + string val; + if(pos != string::npos && txt.at(pos) == '(') { - attribute.condition = select_pseudo_element; - } else + auto end = find_close_bracket(txt, pos); + val = txt.substr(pos + 1, end - pos - 1); + if (end != string::npos) pos = end + 1; + } + + switch (attribute.name) { - attribute.condition = select_pseudo_class; + case _nth_child_: + case _nth_of_type_: + case _nth_last_child_: + case _nth_last_of_type_: + lcase(val); + parse_nth_child_params(val, attribute.a, attribute.b); + break; + case _not_: + attribute.sel = std::make_shared<css_element_selector>(); + attribute.sel->parse(val); + break; + case _lang_: + trim(val); + lcase(val); + attribute.val = val; + break; } - attribute.attribute = _t("pseudo"); + m_attrs.push_back(attribute); el_end = pos; } - } else if(txt[el_end] == _t('#')) + } else if(txt[el_end] == '[') { css_attribute_selector attribute; - tstring::size_type pos = txt.find_first_of(_t(".#[:"), el_end + 1); - attribute.val = txt.substr(el_end + 1, pos - el_end - 1); - attribute.condition = select_equal; - attribute.attribute = _t("id"); - m_attrs.push_back(attribute); - el_end = pos; - } else if(txt[el_end] == _t('[')) - { - css_attribute_selector attribute; - - tstring::size_type pos = txt.find_first_of(_t("]~=|$*^"), el_end + 1); - tstring attr = txt.substr(el_end + 1, pos - el_end - 1); + string::size_type pos = txt.find_first_of("]~=|$*^", el_end + 1); + string attr = txt.substr(el_end + 1, pos - el_end - 1); trim(attr); litehtml::lcase(attr); - if(pos != tstring::npos) + if(pos != string::npos) { - if(txt[pos] == _t(']')) + if(txt[pos] == ']') { - attribute.condition = select_exists; - } else if(txt[pos] == _t('=')) + attribute.type = select_exists; + } else if(txt[pos] == '=') { - attribute.condition = select_equal; + attribute.type = select_equal; pos++; - } else if(txt.substr(pos, 2) == _t("~=")) + } else if(txt.substr(pos, 2) == "~=") { - attribute.condition = select_contain_str; + attribute.type = select_contain_str; pos += 2; - } else if(txt.substr(pos, 2) == _t("|=")) + } else if(txt.substr(pos, 2) == "|=") { - attribute.condition = select_start_str; + attribute.type = select_start_str; pos += 2; - } else if(txt.substr(pos, 2) == _t("^=")) + } else if(txt.substr(pos, 2) == "^=") { - attribute.condition = select_start_str; + attribute.type = select_start_str; pos += 2; - } else if(txt.substr(pos, 2) == _t("$=")) + } else if(txt.substr(pos, 2) == "$=") { - attribute.condition = select_end_str; + attribute.type = select_end_str; pos += 2; - } else if(txt.substr(pos, 2) == _t("*=")) + } else if(txt.substr(pos, 2) == "*=") { - attribute.condition = select_contain_str; + attribute.type = select_contain_str; pos += 2; } else { - attribute.condition = select_exists; + attribute.type = select_exists; pos += 1; } - pos = txt.find_first_not_of(_t(" \t"), pos); - if(pos != tstring::npos) + pos = txt.find_first_not_of(" \t", pos); + if(pos != string::npos) { - if(txt[pos] == _t('"')) + if(txt[pos] == '"') { - tstring::size_type pos2 = txt.find_first_of(_t("\""), pos + 1); - attribute.val = txt.substr(pos + 1, pos2 == tstring::npos ? pos2 : (pos2 - pos - 1)); - pos = pos2 == tstring::npos ? pos2 : (pos2 + 1); - } else if(txt[pos] == _t(']')) + string::size_type pos2 = txt.find_first_of('\"', pos + 1); + attribute.val = txt.substr(pos + 1, pos2 == string::npos ? pos2 : (pos2 - pos - 1)); + pos = pos2 == string::npos ? pos2 : (pos2 + 1); + } else if(txt[pos] == ']') { pos ++; } else { - tstring::size_type pos2 = txt.find_first_of(_t("]"), pos + 1); - attribute.val = txt.substr(pos, pos2 == tstring::npos ? pos2 : (pos2 - pos)); + string::size_type pos2 = txt.find_first_of(']', pos + 1); + attribute.val = txt.substr(pos, pos2 == string::npos ? pos2 : (pos2 - pos)); trim(attribute.val); - pos = pos2 == tstring::npos ? pos2 : (pos2 + 1); + pos = pos2 == string::npos ? pos2 : (pos2 + 1); } } } else { - attribute.condition = select_exists; + attribute.type = select_exists; } - attribute.attribute = attr; + attribute.name = _id(attr); m_attrs.push_back(attribute); el_end = pos; } else { el_end++; } - el_end = txt.find_first_of(_t(".#[:"), el_end); + el_end = txt.find_first_of(".#[:", el_end); } } -bool litehtml::css_selector::parse( const tstring& text ) +bool litehtml::css_selector::parse( const string& text ) { if(text.empty()) { return false; } string_vector tokens; - split_string(text, tokens, _t(""), _t(" \t>+~"), _t("([")); + split_string(text, tokens, "", " \t>+~", "(["); if(tokens.empty()) { return false; } - tstring left; - tstring right = tokens.back(); - tchar_t combinator = 0; + string left; + string right = tokens.back(); + char combinator = 0; tokens.pop_back(); - while(!tokens.empty() && (tokens.back() == _t(" ") || tokens.back() == _t("\t") || tokens.back() == _t("+") || tokens.back() == _t("~") || tokens.back() == _t(">"))) + while(!tokens.empty() && (tokens.back() == " " || tokens.back() == "\t" || tokens.back() == "+" || tokens.back() == "~" || tokens.back() == ">")) { - if(combinator == _t(' ') || combinator == 0) + if(combinator == ' ' || combinator == 0) { combinator = tokens.back()[0]; } tokens.pop_back(); } - for(string_vector::const_iterator i = tokens.begin(); i != tokens.end(); i++) + for(const auto & token : tokens) { - left += *i; + left += token; } trim(left); @@ -200,13 +255,13 @@ bool litehtml::css_selector::parse( const tstring& text ) switch(combinator) { - case _t('>'): + case '>': m_combinator = combinator_child; break; - case _t('+'): + case '+': m_combinator = combinator_adjacent_sibling; break; - case _t('~'): + case '~': m_combinator = combinator_general_sibling; break; default: @@ -214,11 +269,11 @@ bool litehtml::css_selector::parse( const tstring& text ) break; } - m_left = 0; + m_left = nullptr; if(!left.empty()) { - m_left = std::make_shared<css_selector>(media_query_list::ptr(0)); + m_left = std::make_shared<css_selector>(); if(!m_left->parse(left)) { return false; @@ -230,24 +285,18 @@ bool litehtml::css_selector::parse( const tstring& text ) void litehtml::css_selector::calc_specificity() { - if(!m_right.m_tag.empty() && m_right.m_tag != _t("*")) + if(m_right.m_tag != star_id) { m_specificity.d = 1; } - for(css_attribute_selector::vector::iterator i = m_right.m_attrs.begin(); i != m_right.m_attrs.end(); i++) + for(const auto& attr : m_right.m_attrs) { - if(i->attribute == _t("id")) + if(attr.type == select_id) { m_specificity.b++; } else { - if(i->attribute == _t("class")) - { - m_specificity.c += (int) i->class_val.size(); - } else - { - m_specificity.c++; - } + m_specificity.c++; } } if(m_left) diff --git a/src/plugins/litehtml_viewer/litehtml/css_selector.h b/src/plugins/litehtml_viewer/litehtml/css_selector.h @@ -15,7 +15,7 @@ namespace litehtml int c; int d; - selector_specificity(int va = 0, int vb = 0, int vc = 0, int vd = 0) + explicit selector_specificity(int va = 0, int vb = 0, int vc = 0, int vd = 0) { a = va; b = vb; @@ -117,31 +117,41 @@ namespace litehtml ////////////////////////////////////////////////////////////////////////// - enum attr_select_condition + enum attr_select_type { + select_class, + select_id, + select_exists, select_equal, select_contain_str, select_start_str, select_end_str, + select_pseudo_class, select_pseudo_element, }; ////////////////////////////////////////////////////////////////////////// + class css_element_selector; + struct css_attribute_selector { typedef std::vector<css_attribute_selector> vector; - tstring attribute; - tstring val; - string_vector class_val; - attr_select_condition condition; + attr_select_type type; + string_id name; // .name, #name, [name], :name + string val; // [name=val], :lang(val) + + std::shared_ptr<css_element_selector> sel; // :not(sel) + int a, b; // :nth-child(an+b) css_attribute_selector() { - condition = select_exists; + type = select_class; + name = empty_id; + a = b = 0; } }; @@ -150,11 +160,12 @@ namespace litehtml class css_element_selector { public: - tstring m_tag; + string_id m_tag; css_attribute_selector::vector m_attrs; public: - void parse(const tstring& txt); + void parse(const string& txt); + static void parse_nth_child_params(const string& param, int& num, int& off); }; ////////////////////////////////////////////////////////////////////////// @@ -183,26 +194,24 @@ namespace litehtml int m_order; media_query_list::ptr m_media_query; public: - css_selector(media_query_list::ptr media) + explicit css_selector(const media_query_list::ptr& media = nullptr) { m_media_query = media; m_combinator = combinator_descendant; m_order = 0; } - ~css_selector() - { - } + ~css_selector() = default; css_selector(const css_selector& val) { m_right = val.m_right; if(val.m_left) { - m_left = std::make_shared<css_selector>(*val.m_left); + m_left = std::make_shared<css_selector>(*val.m_left); } else { - m_left = 0; + m_left = nullptr; } m_combinator = val.m_combinator; m_specificity = val.m_specificity; @@ -210,7 +219,7 @@ namespace litehtml m_media_query = val.m_media_query; } - bool parse(const tstring& text); + bool parse(const string& text); void calc_specificity(); bool is_media_valid() const; void add_media_to_doc(document* doc) const; @@ -246,7 +255,7 @@ namespace litehtml return (v1.m_specificity < v2.m_specificity); } - inline bool operator >(const css_selector::ptr& v1, const css_selector::ptr& v2) + inline bool operator > (const css_selector::ptr& v1, const css_selector::ptr& v2) { return (*v1 > *v2); } @@ -272,6 +281,19 @@ namespace litehtml m_used = used; m_selector = selector; } + + used_selector(const used_selector& val) + { + m_used = val.m_used; + m_selector = val.m_selector; + } + + used_selector& operator=(const used_selector& val) + { + m_used = val.m_used; + m_selector = val.m_selector; + return *this; + } }; } diff --git a/src/plugins/litehtml_viewer/litehtml/document.cpp b/src/plugins/litehtml_viewer/litehtml/document.cpp @@ -21,43 +21,37 @@ #include "el_div.h" #include "el_font.h" #include "el_tr.h" -#include "el_li.h" -#include <math.h> -#include <stdio.h> +#include <cmath> +#include <cstdio> #include <algorithm> #include "gumbo.h" #include "utf8_strings.h" +#include "render_item.h" -litehtml::document::document(litehtml::document_container* objContainer, litehtml::context* ctx) +litehtml::document::document(document_container* objContainer) { m_container = objContainer; - m_context = ctx; } litehtml::document::~document() { - m_over_element = 0; + m_over_element = nullptr; if(m_container) { - for(fonts_map::iterator f = m_fonts.begin(); f != m_fonts.end(); f++) + for(auto& font : m_fonts) { - m_container->delete_font(f->second.font); + m_container->delete_font(font.second.font); } } } -litehtml::document::ptr litehtml::document::createFromString( const tchar_t* str, litehtml::document_container* objPainter, litehtml::context* ctx, litehtml::css* user_styles) -{ - return createFromUTF8(litehtml_to_utf8(str), objPainter, ctx, user_styles); -} - -litehtml::document::ptr litehtml::document::createFromUTF8(const char* str, litehtml::document_container* objPainter, litehtml::context* ctx, litehtml::css* user_styles) +litehtml::document::ptr litehtml::document::createFromString( const char* str, document_container* objPainter, const char* master_styles, const char* user_styles ) { // parse document into GumboOutput - GumboOutput* output = gumbo_parse((const char*) str); + GumboOutput* output = gumbo_parse(str); // Create litehtml::document - litehtml::document::ptr doc = std::make_shared<litehtml::document>(objPainter, ctx); + document::ptr doc = std::make_shared<document>(objPainter); // Create litehtml::elements. elements_vector root_elements; @@ -69,30 +63,43 @@ litehtml::document::ptr litehtml::document::createFromUTF8(const char* str, lite // Destroy GumboOutput gumbo_destroy_output(&kGumboDefaultOptions, output); + if (master_styles && *master_styles) + { + doc->m_master_css.parse_stylesheet(master_styles, nullptr, doc, nullptr); + doc->m_master_css.sort_selectors(); + } + if (user_styles && *user_styles) + { + doc->m_user_css.parse_stylesheet(user_styles, nullptr, doc, nullptr); + doc->m_user_css.sort_selectors(); + } + // Let's process created elements tree if (doc->m_root) { doc->container()->get_media_features(doc->m_media); + doc->m_root->set_pseudo_class(_root_, true); + // apply master CSS - doc->m_root->apply_stylesheet(ctx->master_css()); + doc->m_root->apply_stylesheet(doc->m_master_css); // parse elements attributes doc->m_root->parse_attributes(); // parse style sheets linked in document media_query_list::ptr media; - for (css_text::vector::iterator css = doc->m_css.begin(); css != doc->m_css.end(); css++) + for (const auto& css : doc->m_css) { - if (!css->media.empty()) + if (!css.media.empty()) { - media = media_query_list::create_from_string(css->media, doc); + media = media_query_list::create_from_string(css.media, doc); } else { - media = 0; + media = nullptr; } - doc->m_styles.parse_stylesheet(css->text.c_str(), css->baseurl.c_str(), doc, media); + doc->m_styles.parse_stylesheet(css.text.c_str(), css.baseurl.c_str(), doc, media); } // Sort css selectors using CSS rules. doc->m_styles.sort_selectors(); @@ -107,77 +114,100 @@ litehtml::document::ptr litehtml::document::createFromUTF8(const char* str, lite doc->m_root->apply_stylesheet(doc->m_styles); // Apply user styles if any - if (user_styles) - { - doc->m_root->apply_stylesheet(*user_styles); - } + doc->m_root->apply_stylesheet(doc->m_user_css); - // Parse applied styles in the elements - doc->m_root->parse_styles(); + // Initialize m_css + doc->m_root->compute_styles(); + + // Create rendering tree + doc->m_root_render = doc->m_root->create_render_item(nullptr); // Now the m_tabular_elements is filled with tabular elements. // We have to check the tabular elements for missing table elements // and create the anonymous boxes in visual table layout doc->fix_tables_layout(); - // Fanaly initialize elements - doc->m_root->init(); + // Finally initialize elements + // init() return pointer to the render_init element because it can change its type + doc->m_root_render = doc->m_root_render->init(); } return doc; } -litehtml::uint_ptr litehtml::document::add_font( const tchar_t* name, int size, const tchar_t* weight, const tchar_t* style, const tchar_t* decoration, font_metrics* fm ) +litehtml::uint_ptr litehtml::document::add_font( const char* name, int size, const char* weight, const char* style, const char* decoration, font_metrics* fm ) { uint_ptr ret = 0; - if( !name || (name && !t_strcasecmp(name, _t("inherit"))) ) + if(!name) { name = m_container->get_default_font_name(); } - if(!size) - { - size = container()->get_default_font_size(); - } - - tchar_t strSize[20]; + char strSize[20]; t_itoa(size, strSize, 20, 10); - tstring key = name; - key += _t(":"); + string key = name; + key += ":"; key += strSize; - key += _t(":"); + key += ":"; key += weight; - key += _t(":"); + key += ":"; key += style; - key += _t(":"); + key += ":"; key += decoration; if(m_fonts.find(key) == m_fonts.end()) { - font_style fs = (font_style) value_index(style, font_style_strings, fontStyleNormal); + font_style fs = (font_style) value_index(style, font_style_strings, font_style_normal); int fw = value_index(weight, font_weight_strings, -1); if(fw >= 0) { switch(fw) { - case litehtml::fontWeightBold: + case litehtml::font_weight_bold: fw = 700; break; - case litehtml::fontWeightBolder: + case litehtml::font_weight_bolder: fw = 600; break; - case litehtml::fontWeightLighter: + case litehtml::font_weight_lighter: + fw = 300; + break; + case litehtml::font_weight_normal: + fw = 400; + break; + case litehtml::font_weight_100: + fw = 100; + break; + case litehtml::font_weight_200: + fw = 200; + break; + case litehtml::font_weight_300: fw = 300; break; - default: + case litehtml::font_weight_400: fw = 400; break; + case litehtml::font_weight_500: + fw = 500; + break; + case litehtml::font_weight_600: + fw = 600; + break; + case litehtml::font_weight_700: + fw = 700; + break; + case litehtml::font_weight_800: + fw = 800; + break; + case litehtml::font_weight_900: + fw = 900; + break; } } else { - fw = t_atoi(weight); + fw = atoi(weight); if(fw < 100) { fw = 400; @@ -188,17 +218,17 @@ litehtml::uint_ptr litehtml::document::add_font( const tchar_t* name, int size, if(decoration) { - std::vector<tstring> tokens; - split_string(decoration, tokens, _t(" ")); - for(std::vector<tstring>::iterator i = tokens.begin(); i != tokens.end(); i++) + std::vector<string> tokens; + split_string(decoration, tokens, " "); + for(auto & token : tokens) { - if(!t_strcasecmp(i->c_str(), _t("underline"))) + if(!t_strcasecmp(token.c_str(), "underline")) { decor |= font_decoration_underline; - } else if(!t_strcasecmp(i->c_str(), _t("line-through"))) + } else if(!t_strcasecmp(token.c_str(), "line-through")) { decor |= font_decoration_linethrough; - } else if(!t_strcasecmp(i->c_str(), _t("overline"))) + } else if(!t_strcasecmp(token.c_str(), "overline")) { decor |= font_decoration_overline; } @@ -218,32 +248,31 @@ litehtml::uint_ptr litehtml::document::add_font( const tchar_t* name, int size, return ret; } -litehtml::uint_ptr litehtml::document::get_font( const tchar_t* name, int size, const tchar_t* weight, const tchar_t* style, const tchar_t* decoration, font_metrics* fm ) +litehtml::uint_ptr litehtml::document::get_font( const char* name, int size, const char* weight, const char* style, const char* decoration, font_metrics* fm ) { - if( !name || (name && !t_strcasecmp(name, _t("inherit"))) ) + if(!size) { - name = m_container->get_default_font_name(); + return 0; } - - if(!size) + if(!name) { - size = m_container->get_default_font_size(); + name = m_container->get_default_font_name(); } - tchar_t strSize[20]; + char strSize[20]; t_itoa(size, strSize, 20, 10); - tstring key = name; - key += _t(":"); + string key = name; + key += ":"; key += strSize; - key += _t(":"); + key += ":"; key += weight; - key += _t(":"); + key += ":"; key += style; - key += _t(":"); + key += ":"; key += decoration; - fonts_map::iterator el = m_fonts.find(key); + auto el = m_fonts.find(key); if(el != m_fonts.end()) { @@ -261,21 +290,31 @@ int litehtml::document::render( int max_width, render_type rt ) int ret = 0; if(m_root) { + position client_rc; + m_container->get_client_rect(client_rc); + containing_block_context cb_context; + cb_context.width = max_width; + cb_context.width.type = containing_block_context::cbc_value_type_absolute; + cb_context.height = client_rc.height; + cb_context.height.type = containing_block_context::cbc_value_type_absolute; + if(rt == render_fixed_only) { m_fixed_boxes.clear(); - m_root->render_positioned(rt); + m_root_render->render_positioned(rt); } else { - ret = m_root->render(0, 0, max_width); - if(m_root->fetch_positioned()) + ret = m_root_render->render(0, 0, cb_context); + if(m_root_render->fetch_positioned()) { m_fixed_boxes.clear(); - m_root->render_positioned(rt); + m_root_render->render_positioned(rt); } m_size.width = 0; m_size.height = 0; - m_root->calc_document_size(m_size); + m_content_size.width = 0; + m_content_size.height = 0; + m_root_render->calc_document_size(m_size, m_content_size); } } return ret; @@ -283,14 +322,14 @@ int litehtml::document::render( int max_width, render_type rt ) void litehtml::document::draw( uint_ptr hdc, int x, int y, const position* clip ) { - if(m_root) + if(m_root && m_root_render) { - m_root->draw(hdc, x, y, clip); - m_root->draw_stacking_context(hdc, x, y, clip, true); + m_root->draw(hdc, x, y, clip, m_root_render); + m_root_render->draw_stacking_context(hdc, x, y, clip, true); } } -int litehtml::document::cvt_units( const tchar_t* str, int fontSize, bool* is_percent/*= 0*/ ) const +int litehtml::document::to_pixels( const char* str, int fontSize, bool* is_percent/*= 0*/ ) const { if(!str) return 0; @@ -300,40 +339,35 @@ int litehtml::document::cvt_units( const tchar_t* str, int fontSize, bool* is_pe { *is_percent = true; } - return cvt_units(val, fontSize); + return to_pixels(val, fontSize); } -int litehtml::document::cvt_units( css_length& val, int fontSize, int size ) const +int litehtml::document::to_pixels( const css_length& val, int fontSize, int size ) const { if(val.is_predefined()) { return 0; } - int ret = 0; + int ret; switch(val.units()) { case css_units_percentage: ret = val.calc_percent(size); break; case css_units_em: - ret = round_f(val.val() * fontSize); - val.set_value((float) ret, css_units_px); + ret = round_f(val.val() * (float) fontSize); break; case css_units_pt: ret = m_container->pt_to_px((int) val.val()); - val.set_value((float) ret, css_units_px); break; case css_units_in: ret = m_container->pt_to_px((int) (val.val() * 72)); - val.set_value((float) ret, css_units_px); break; case css_units_cm: ret = m_container->pt_to_px((int) (val.val() * 0.3937 * 72)); - val.set_value((float) ret, css_units_px); break; case css_units_mm: ret = m_container->pt_to_px((int) (val.val() * 0.3937 * 72) / 10); - val.set_value((float) ret, css_units_px); break; case css_units_vw: ret = (int)((double)m_media.width * (double)val.val() / 100.0); @@ -348,8 +382,7 @@ int litehtml::document::cvt_units( css_length& val, int fontSize, int size ) con ret = (int)((double)std::max(m_media.height, m_media.width) * (double)val.val() / 100.0); break; case css_units_rem: - ret = (int) ((double) m_root->get_font_size() * (double) val.val()); - val.set_value((float) ret, css_units_px); + ret = (int) ((double) m_root->css().get_font_size() * (double) val.val()); break; default: ret = (int) val.val(); @@ -358,6 +391,38 @@ int litehtml::document::cvt_units( css_length& val, int fontSize, int size ) con return ret; } +void litehtml::document::cvt_units( css_length& val, int fontSize, int size ) const +{ + if(val.is_predefined()) + { + return; + } + int ret; + switch(val.units()) + { + case css_units_em: + ret = round_f(val.val() * (float) fontSize); + val.set_value((float) ret, css_units_px); + break; + case css_units_pt: + ret = m_container->pt_to_px((int) val.val()); + val.set_value((float) ret, css_units_px); + break; + case css_units_in: + ret = m_container->pt_to_px((int) (val.val() * 72)); + val.set_value((float) ret, css_units_px); + break; + case css_units_cm: + ret = m_container->pt_to_px((int) (val.val() * 0.3937 * 72)); + val.set_value((float) ret, css_units_px); + break; + case css_units_mm: + ret = m_container->pt_to_px((int) (val.val() * 0.3937 * 72) / 10); + val.set_value((float) ret, css_units_px); + break; + } +} + int litehtml::document::width() const { return m_size.width; @@ -368,7 +433,18 @@ int litehtml::document::height() const return m_size.height; } -void litehtml::document::add_stylesheet( const tchar_t* str, const tchar_t* baseurl, const tchar_t* media ) +int litehtml::document::content_width() const +{ + return m_content_size.width; +} + +int litehtml::document::content_height() const +{ + return m_content_size.height; +} + + +void litehtml::document::add_stylesheet( const char* str, const char* baseurl, const char* media ) { if(str && str[0]) { @@ -378,12 +454,12 @@ void litehtml::document::add_stylesheet( const tchar_t* str, const tchar_t* base bool litehtml::document::on_mouse_over( int x, int y, int client_x, int client_y, position::vector& redraw_boxes ) { - if(!m_root) + if(!m_root || !m_root_render) { return false; } - element::ptr over_el = m_root->get_element_by_point(x, y, client_x, client_y); + element::ptr over_el = m_root_render->get_element_by_point(x, y, client_x, client_y); bool state_was_changed = false; @@ -399,7 +475,7 @@ bool litehtml::document::on_mouse_over( int x, int y, int client_x, int client_y m_over_element = over_el; } - const tchar_t* cursor = 0; + string cursor; if(m_over_element) { @@ -407,21 +483,21 @@ bool litehtml::document::on_mouse_over( int x, int y, int client_x, int client_y { state_was_changed = true; } - cursor = m_over_element->get_cursor(); + cursor = m_over_element->css().get_cursor(); } - m_container->set_cursor(cursor ? cursor : _t("auto")); + m_container->set_cursor(cursor.c_str()); if(state_was_changed) { - return m_root->find_styles_changes(redraw_boxes, 0, 0); + return m_root->find_styles_changes(redraw_boxes); } return false; } bool litehtml::document::on_mouse_leave( position::vector& redraw_boxes ) { - if(!m_root) + if(!m_root || !m_root_render) { return false; } @@ -429,7 +505,7 @@ bool litehtml::document::on_mouse_leave( position::vector& redraw_boxes ) { if(m_over_element->on_mouse_leave()) { - return m_root->find_styles_changes(redraw_boxes, 0, 0); + return m_root->find_styles_changes(redraw_boxes); } } return false; @@ -437,12 +513,12 @@ bool litehtml::document::on_mouse_leave( position::vector& redraw_boxes ) bool litehtml::document::on_lbutton_down( int x, int y, int client_x, int client_y, position::vector& redraw_boxes ) { - if(!m_root) + if(!m_root || !m_root_render) { return false; } - element::ptr over_el = m_root->get_element_by_point(x, y, client_x, client_y); + element::ptr over_el = m_root_render->get_element_by_point(x, y, client_x, client_y); bool state_was_changed = false; @@ -465,7 +541,7 @@ bool litehtml::document::on_lbutton_down( int x, int y, int client_x, int client } } - const tchar_t* cursor = 0; + string cursor; if(m_over_element) { @@ -473,14 +549,14 @@ bool litehtml::document::on_lbutton_down( int x, int y, int client_x, int client { state_was_changed = true; } - cursor = m_over_element->get_cursor(); + cursor = m_over_element->css().get_cursor(); } - m_container->set_cursor(cursor ? cursor : _t("auto")); + m_container->set_cursor(cursor.c_str()); if(state_was_changed) { - return m_root->find_styles_changes(redraw_boxes, 0, 0); + return m_root->find_styles_changes(redraw_boxes); } return false; @@ -488,7 +564,7 @@ bool litehtml::document::on_lbutton_down( int x, int y, int client_x, int client bool litehtml::document::on_lbutton_up( int x, int y, int client_x, int client_y, position::vector& redraw_boxes ) { - if(!m_root) + if(!m_root || !m_root_render) { return false; } @@ -496,13 +572,13 @@ bool litehtml::document::on_lbutton_up( int x, int y, int client_x, int client_y { if(m_over_element->on_lbutton_up()) { - return m_root->find_styles_changes(redraw_boxes, 0, 0); + return m_root->find_styles_changes(redraw_boxes); } } return false; } -litehtml::element::ptr litehtml::document::create_element(const tchar_t* tag_name, const string_map& attributes) +litehtml::element::ptr litehtml::document::create_element(const char* tag_name, const string_map& attributes) { element::ptr newTag; document::ptr this_doc = shared_from_this(); @@ -512,54 +588,51 @@ litehtml::element::ptr litehtml::document::create_element(const tchar_t* tag_nam } if(!newTag) { - if(!t_strcmp(tag_name, _t("br"))) + if(!strcmp(tag_name, "br")) { newTag = std::make_shared<litehtml::el_break>(this_doc); - } else if(!t_strcmp(tag_name, _t("p"))) + } else if(!strcmp(tag_name, "p")) { newTag = std::make_shared<litehtml::el_para>(this_doc); - } else if(!t_strcmp(tag_name, _t("img"))) + } else if(!strcmp(tag_name, "img")) { newTag = std::make_shared<litehtml::el_image>(this_doc); - } else if(!t_strcmp(tag_name, _t("table"))) + } else if(!strcmp(tag_name, "table")) { newTag = std::make_shared<litehtml::el_table>(this_doc); - } else if(!t_strcmp(tag_name, _t("td")) || !t_strcmp(tag_name, _t("th"))) + } else if(!strcmp(tag_name, "td") || !strcmp(tag_name, "th")) { newTag = std::make_shared<litehtml::el_td>(this_doc); - } else if(!t_strcmp(tag_name, _t("link"))) + } else if(!strcmp(tag_name, "link")) { newTag = std::make_shared<litehtml::el_link>(this_doc); - } else if(!t_strcmp(tag_name, _t("title"))) + } else if(!strcmp(tag_name, "title")) { newTag = std::make_shared<litehtml::el_title>(this_doc); - } else if(!t_strcmp(tag_name, _t("a"))) + } else if(!strcmp(tag_name, "a")) { newTag = std::make_shared<litehtml::el_anchor>(this_doc); - } else if(!t_strcmp(tag_name, _t("tr"))) + } else if(!strcmp(tag_name, "tr")) { newTag = std::make_shared<litehtml::el_tr>(this_doc); - } else if(!t_strcmp(tag_name, _t("style"))) + } else if(!strcmp(tag_name, "style")) { newTag = std::make_shared<litehtml::el_style>(this_doc); - } else if(!t_strcmp(tag_name, _t("base"))) + } else if(!strcmp(tag_name, "base")) { newTag = std::make_shared<litehtml::el_base>(this_doc); - } else if(!t_strcmp(tag_name, _t("body"))) + } else if(!strcmp(tag_name, "body")) { newTag = std::make_shared<litehtml::el_body>(this_doc); - } else if(!t_strcmp(tag_name, _t("div"))) + } else if(!strcmp(tag_name, "div")) { newTag = std::make_shared<litehtml::el_div>(this_doc); - } else if(!t_strcmp(tag_name, _t("script"))) + } else if(!strcmp(tag_name, "script")) { newTag = std::make_shared<litehtml::el_script>(this_doc); - } else if(!t_strcmp(tag_name, _t("font"))) + } else if(!strcmp(tag_name, "font")) { newTag = std::make_shared<litehtml::el_font>(this_doc); - } else if(!t_strcmp(tag_name, _t("li"))) - { - newTag = std::make_shared<litehtml::el_li>(this_doc); } else { newTag = std::make_shared<litehtml::html_tag>(this_doc); @@ -569,9 +642,9 @@ litehtml::element::ptr litehtml::document::create_element(const tchar_t* tag_nam if(newTag) { newTag->set_tagName(tag_name); - for (string_map::const_iterator iter = attributes.begin(); iter != attributes.end(); iter++) + for (const auto & attribute : attributes) { - newTag->set_attr(iter->first.c_str(), iter->second.c_str()); + newTag->set_attr(attribute.first.c_str(), attribute.second.c_str()); } } @@ -590,15 +663,12 @@ void litehtml::document::add_fixed_box( const position& pos ) bool litehtml::document::media_changed() { - if(!m_media_lists.empty()) + container()->get_media_features(m_media); + if (update_media_lists(m_media)) { - container()->get_media_features(m_media); - if (update_media_lists(m_media)) - { - m_root->refresh_styles(); - m_root->parse_styles(); - return true; - } + m_root->refresh_styles(); + m_root->compute_styles(); + return true; } return false; } @@ -607,18 +677,18 @@ bool litehtml::document::lang_changed() { if(!m_media_lists.empty()) { - tstring culture; + string culture; container()->get_language(m_lang, culture); if(!culture.empty()) { - m_culture = m_lang + _t('-') + culture; + m_culture = m_lang + '-' + culture; } else { m_culture.clear(); } m_root->refresh_styles(); - m_root->parse_styles(); + m_root->compute_styles(); return true; } return false; @@ -627,9 +697,9 @@ bool litehtml::document::lang_changed() bool litehtml::document::update_media_lists(const media_features& features) { bool update_styles = false; - for(media_query_list::vector::iterator iter = m_media_lists.begin(); iter != m_media_lists.end(); iter++) + for(auto & m_media_list : m_media_lists) { - if((*iter)->apply_media_features(features)) + if(m_media_list->apply_media_features(features)) { update_styles = true; } @@ -637,7 +707,7 @@ bool litehtml::document::update_media_lists(const media_features& features) return update_styles; } -void litehtml::document::add_media_list( media_query_list::ptr list ) +void litehtml::document::add_media_list( const media_query_list::ptr& list ) { if(list) { @@ -650,7 +720,7 @@ void litehtml::document::add_media_list( media_query_list::ptr list ) void litehtml::document::create_node(void* gnode, elements_vector& elements, bool parseTextNode) { - GumboNode* node = (GumboNode*)gnode; + auto* node = (GumboNode*)gnode; switch (node->type) { case GUMBO_NODE_ELEMENT: @@ -660,7 +730,7 @@ void litehtml::document::create_node(void* gnode, elements_vector& elements, boo for (unsigned int i = 0; i < node->v.element.attributes.length; i++) { attr = (GumboAttribute*)node->v.element.attributes.data[i]; - attrs[tstring(litehtml_from_utf8(attr->name))] = litehtml_from_utf8(attr->value); + attrs[attr->name] = attr->value; } @@ -668,7 +738,7 @@ void litehtml::document::create_node(void* gnode, elements_vector& elements, boo const char* tag = gumbo_normalized_tagname(node->v.element.tag); if (tag[0]) { - ret = create_element(litehtml_from_utf8(tag), attrs); + ret = create_element(tag, attrs); } else { @@ -677,7 +747,7 @@ void litehtml::document::create_node(void* gnode, elements_vector& elements, boo std::string strA; gumbo_tag_from_original_text(&node->v.element.original_tag); strA.append(node->v.element.original_tag.data, node->v.element.original_tag.length); - ret = create_element(litehtml_from_utf8(strA.c_str()), attrs); + ret = create_element(strA.c_str(), attrs); } } if (!strcmp(tag, "script")) @@ -704,68 +774,35 @@ void litehtml::document::create_node(void* gnode, elements_vector& elements, boo break; case GUMBO_NODE_TEXT: { - std::wstring str; - std::wstring str_in = (const wchar_t*) (utf8_to_wchar(node->v.text.text)); if (!parseTextNode) { - elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str_in.c_str()), shared_from_this())); - break; + elements.push_back(std::make_shared<el_text>(node->v.text.text, shared_from_this())); } - ucode_t c; - for (size_t i = 0; i < str_in.length(); i++) - { - c = (ucode_t) str_in[i]; - if (c <= ' ' && (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f')) - { - if (!str.empty()) - { - elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), shared_from_this())); - str.clear(); - } - str += c; - elements.push_back(std::make_shared<el_space>(litehtml_from_wchar(str.c_str()), shared_from_this())); - str.clear(); - } - // CJK character range - else if (c >= 0x4E00 && c <= 0x9FCC) - { - if (!str.empty()) - { - elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), shared_from_this())); - str.clear(); - } - str += c; - elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), shared_from_this())); - str.clear(); - } - else - { - str += c; - } - } - if (!str.empty()) + else { - elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), shared_from_this())); + m_container->split_text(node->v.text.text, + [this, &elements](const char* text) { elements.push_back(std::make_shared<el_text>(text, shared_from_this())); }, + [this, &elements](const char* text) { elements.push_back(std::make_shared<el_space>(text, shared_from_this())); }); } } break; case GUMBO_NODE_CDATA: { element::ptr ret = std::make_shared<el_cdata>(shared_from_this()); - ret->set_data(litehtml_from_utf8(node->v.text.text)); + ret->set_data(node->v.text.text); elements.push_back(ret); } break; case GUMBO_NODE_COMMENT: { element::ptr ret = std::make_shared<el_comment>(shared_from_this()); - ret->set_data(litehtml_from_utf8(node->v.text.text)); + ret->set_data(node->v.text.text); elements.push_back(ret); } break; case GUMBO_NODE_WHITESPACE: { - tstring str = litehtml_from_utf8(node->v.text.text); + string str = node->v.text.text; for (size_t i = 0; i < str.length(); i++) { elements.push_back(std::make_shared<el_space>(str.substr(i, 1).c_str(), shared_from_this())); @@ -779,36 +816,33 @@ void litehtml::document::create_node(void* gnode, elements_vector& elements, boo void litehtml::document::fix_tables_layout() { - size_t i = 0; - while (i < m_tabular_elements.size()) + for (const auto& el_ptr : m_tabular_elements) { - element::ptr el_ptr = m_tabular_elements[i]; - - switch (el_ptr->get_display()) + switch (el_ptr->src_el()->css().get_display()) { case display_inline_table: case display_table: - fix_table_children(el_ptr, display_table_row_group, _t("table-row-group")); + fix_table_children(el_ptr, display_table_row_group, "table-row-group"); break; case display_table_footer_group: case display_table_row_group: case display_table_header_group: { - element::ptr parent = el_ptr->parent(); + auto parent = el_ptr->parent(); if (parent) { - if (parent->get_display() != display_inline_table) - fix_table_parent(el_ptr, display_table, _t("table")); + if (parent->src_el()->css().get_display() != display_inline_table) + fix_table_parent(el_ptr, display_table, "table"); } - fix_table_children(el_ptr, display_table_row, _t("table-row")); + fix_table_children(el_ptr, display_table_row, "table-row"); } break; case display_table_row: - fix_table_parent(el_ptr, display_table_row_group, _t("table-row-group")); - fix_table_children(el_ptr, display_table_cell, _t("table-cell")); + fix_table_parent(el_ptr, display_table_row_group, "table-row-group"); + fix_table_children(el_ptr, display_table_cell, "table-cell"); break; case display_table_cell: - fix_table_parent(el_ptr, display_table_row, _t("table-row")); + fix_table_parent(el_ptr, display_table_row, "table-row"); break; // TODO: make table layout fix for table-caption, table-column etc. elements case display_table_caption: @@ -817,51 +851,61 @@ void litehtml::document::fix_tables_layout() default: break; } - i++; } } -void litehtml::document::fix_table_children(element::ptr& el_ptr, style_display disp, const tchar_t* disp_str) +void litehtml::document::fix_table_children(const std::shared_ptr<render_item>& el_ptr, style_display disp, const char* disp_str) { - elements_vector tmp; - elements_vector::iterator first_iter = el_ptr->m_children.begin(); - elements_vector::iterator cur_iter = el_ptr->m_children.begin(); + std::list<std::shared_ptr<render_item>> tmp; + auto first_iter = el_ptr->children().begin(); + auto cur_iter = el_ptr->children().begin(); auto flush_elements = [&]() { - element::ptr annon_tag = std::make_shared<html_tag>(shared_from_this()); - style st; - st.add_property(_t("display"), disp_str, 0, false); - annon_tag->add_style(st); - annon_tag->parent(el_ptr); - annon_tag->parse_styles(); - std::for_each(tmp.begin(), tmp.end(), - [&annon_tag](element::ptr& el) - { - annon_tag->appendChild(el); - } - ); - first_iter = el_ptr->m_children.insert(first_iter, annon_tag); - cur_iter = first_iter + 1; - while (cur_iter != el_ptr->m_children.end() && (*cur_iter)->parent() != el_ptr) + element::ptr annon_tag = std::make_shared<html_tag>(el_ptr->src_el(), string("display:") + disp_str); + std::shared_ptr<render_item> annon_ri; + if(annon_tag->css().get_display() == display_table_cell) + { + annon_tag->set_tagName("table_cell"); + annon_ri = std::make_shared<render_item_block>(annon_tag); + } else if(annon_tag->css().get_display() == display_table_row) + { + annon_ri = std::make_shared<render_item_table_row>(annon_tag); + } else + { + annon_ri = std::make_shared<render_item_table_part>(annon_tag); + } + for(const auto& el : tmp) + { + annon_ri->add_child(el); + } + // add annon item as tabular for future processing + add_tabular(annon_ri); + annon_ri->parent(el_ptr); + first_iter = el_ptr->children().insert(first_iter, annon_ri); + cur_iter = std::next(first_iter); + while (cur_iter != el_ptr->children().end() && (*cur_iter)->parent() != el_ptr) { - cur_iter = el_ptr->m_children.erase(cur_iter); + cur_iter = el_ptr->children().erase(cur_iter); } first_iter = cur_iter; tmp.clear(); }; - while (cur_iter != el_ptr->m_children.end()) + while (cur_iter != el_ptr->children().end()) { - if ((*cur_iter)->get_display() != disp) + if ((*cur_iter)->src_el()->css().get_display() != disp) { - if (!(*cur_iter)->is_white_space() || ((*cur_iter)->is_white_space() && !tmp.empty())) + if (!(*cur_iter)->src_el()->is_table_skip() || ((*cur_iter)->src_el()->is_table_skip() && !tmp.empty())) { - if (tmp.empty()) + if (disp != display_table_row_group || (*cur_iter)->src_el()->css().get_display() != display_table_caption) { - first_iter = cur_iter; + if (tmp.empty()) + { + first_iter = cur_iter; + } + tmp.push_back((*cur_iter)); } - tmp.push_back((*cur_iter)); } cur_iter++; } @@ -880,14 +924,14 @@ void litehtml::document::fix_table_children(element::ptr& el_ptr, style_display } } -void litehtml::document::fix_table_parent(element::ptr& el_ptr, style_display disp, const tchar_t* disp_str) +void litehtml::document::fix_table_parent(const std::shared_ptr<render_item>& el_ptr, style_display disp, const char* disp_str) { - element::ptr parent = el_ptr->parent(); + auto parent = el_ptr->parent(); - if (parent->get_display() != disp) + if (parent->src_el()->css().get_display() != disp) { - elements_vector::iterator this_element = std::find_if(parent->m_children.begin(), parent->m_children.end(), - [&](element::ptr& el) + auto this_element = std::find_if(parent->children().begin(), parent->children().end(), + [&](const std::shared_ptr<render_item>& el) { if (el == el_ptr) { @@ -896,19 +940,19 @@ void litehtml::document::fix_table_parent(element::ptr& el_ptr, style_display di return false; } ); - if (this_element != parent->m_children.end()) + if (this_element != parent->children().end()) { - style_display el_disp = el_ptr->get_display(); - elements_vector::iterator first = this_element; - elements_vector::iterator last = this_element; - elements_vector::iterator cur = this_element; + style_display el_disp = el_ptr->src_el()->css().get_display(); + auto first = this_element; + auto last = this_element; + auto cur = this_element; // find first element with same display while (true) { - if (cur == parent->m_children.begin()) break; + if (cur == parent->children().begin()) break; cur--; - if ((*cur)->is_white_space() || (*cur)->get_display() == el_disp) + if ((*cur)->src_el()->is_table_skip() || (*cur)->src_el()->css().get_display() == el_disp) { first = cur; } @@ -923,9 +967,9 @@ void litehtml::document::fix_table_parent(element::ptr& el_ptr, style_display di while (true) { cur++; - if (cur == parent->m_children.end()) break; + if (cur == parent->children().end()) break; - if ((*cur)->is_white_space() || (*cur)->get_display() == el_disp) + if ((*cur)->src_el()->is_table_skip() || (*cur)->src_el()->css().get_display() == el_disp) { last = cur; } @@ -936,30 +980,33 @@ void litehtml::document::fix_table_parent(element::ptr& el_ptr, style_display di } // extract elements with the same display and wrap them with anonymous object - element::ptr annon_tag = std::make_shared<html_tag>(shared_from_this()); - style st; - st.add_property(_t("display"), disp_str, 0, false); - annon_tag->add_style(st); - annon_tag->parent(parent); - annon_tag->parse_styles(); - std::for_each(first, last + 1, - [&annon_tag](element::ptr& el) + element::ptr annon_tag = std::make_shared<html_tag>(parent->src_el(), string("display:") + disp_str); + std::shared_ptr<render_item> annon_ri; + if(annon_tag->css().get_display() == display_table || annon_tag->css().get_display() == display_inline_table) + { + annon_ri = std::make_shared<render_item_table>(annon_tag); + } else if(annon_tag->css().get_display() == display_table_row) + { + annon_ri = std::make_shared<render_item_table_row>(annon_tag); + } else + { + annon_ri = std::make_shared<render_item_table_part>(annon_tag); + } + std::for_each(first, std::next(last, 1), + [&annon_ri](std::shared_ptr<render_item>& el) { - annon_tag->appendChild(el); + annon_ri->add_child(el); } ); - first = parent->m_children.erase(first, last + 1); - parent->m_children.insert(first, annon_tag); + first = parent->children().erase(first, std::next(last)); + parent->children().insert(first, annon_ri); + add_tabular(annon_ri); + annon_ri->parent(parent); } } } -void litehtml::document::append_children_from_string(element& parent, const tchar_t* str) -{ - append_children_from_utf8(parent, litehtml_to_utf8(str)); -} - -void litehtml::document::append_children_from_utf8(element& parent, const char* str) +void litehtml::document::append_children_from_string(element& parent, const char* str) { // parent must belong to this document if (parent.get_document().get() != this) @@ -968,7 +1015,7 @@ void litehtml::document::append_children_from_utf8(element& parent, const char* } // parse document into GumboOutput - GumboOutput* output = gumbo_parse((const char*) str); + GumboOutput* output = gumbo_parse(str); // Create litehtml::elements. elements_vector child_elements; @@ -978,13 +1025,13 @@ void litehtml::document::append_children_from_utf8(element& parent, const char* gumbo_destroy_output(&kGumboDefaultOptions, output); // Let's process created elements tree - for (litehtml::element::ptr child : child_elements) + for (const auto& child : child_elements) { // Add the child element to parent parent.appendChild(child); // apply master CSS - child->apply_stylesheet(m_context->master_css()); + child->apply_stylesheet(m_master_css); // parse elements attributes child->parse_attributes(); @@ -992,15 +1039,26 @@ void litehtml::document::append_children_from_utf8(element& parent, const char* // Apply parsed styles. child->apply_stylesheet(m_styles); - // Parse applied styles in the elements - child->parse_styles(); + // Apply user styles if any + child->apply_stylesheet(m_user_css); + + // Initialize m_css + child->compute_styles(); // Now the m_tabular_elements is filled with tabular elements. // We have to check the tabular elements for missing table elements // and create the anonymous boxes in visual table layout fix_tables_layout(); - // Fanaly initialize elements - child->init(); + // Finally initialize elements + //child->init(); } -} -\ No newline at end of file +} + +void litehtml::document::dump(dumper& cout) +{ + if(m_root_render) + { + m_root_render->dump(cout); + } +} diff --git a/src/plugins/litehtml_viewer/litehtml/document.h b/src/plugins/litehtml_viewer/litehtml/document.h @@ -3,7 +3,7 @@ #include "style.h" #include "types.h" -#include "context.h" +#include "master_css.h" namespace litehtml { @@ -11,19 +11,17 @@ namespace litehtml { typedef std::vector<css_text> vector; - tstring text; - tstring baseurl; - tstring media; + string text; + string baseurl; + string media; - css_text() - { - } + css_text() = default; - css_text(const tchar_t* txt, const tchar_t* url, const tchar_t* media_str) + css_text(const char* txt, const char* url, const char* media_str) { - text = txt ? txt : _t(""); - baseurl = url ? url : _t(""); - media = media_str ? media_str : _t(""); + text = txt ? txt : ""; + baseurl = url ? url : ""; + media = media_str ? media_str : ""; } css_text(const css_text& val) @@ -34,19 +32,19 @@ namespace litehtml } }; - struct stop_tags_t - { - const litehtml::tchar_t* tags; - const litehtml::tchar_t* stop_parent; - }; - - struct ommited_end_tags_t - { - const litehtml::tchar_t* tag; - const litehtml::tchar_t* followed_tags; - }; + class dumper + { + public: + virtual ~dumper() {} + virtual void begin_node(const litehtml::string& descr) = 0; + virtual void end_node() = 0; + virtual void begin_attrs_group(const litehtml::string& descr) = 0; + virtual void end_attrs_group() = 0; + virtual void add_attr(const litehtml::string& name, const litehtml::string& value) = 0; + }; class html_tag; + class render_item; class document : public std::enable_shared_from_this<document> { @@ -55,64 +53,69 @@ namespace litehtml typedef std::weak_ptr<document> weak_ptr; private: std::shared_ptr<element> m_root; + std::shared_ptr<render_item> m_root_render; document_container* m_container; fonts_map m_fonts; css_text::vector m_css; litehtml::css m_styles; litehtml::web_color m_def_color; - litehtml::context* m_context; + litehtml::css m_master_css; + litehtml::css m_user_css; litehtml::size m_size; + litehtml::size m_content_size; position::vector m_fixed_boxes; media_query_list::vector m_media_lists; element::ptr m_over_element; - elements_vector m_tabular_elements; + std::list<std::shared_ptr<render_item>> m_tabular_elements; media_features m_media; - tstring m_lang; - tstring m_culture; + string m_lang; + string m_culture; public: - document(litehtml::document_container* objContainer, litehtml::context* ctx); + document(document_container* objContainer); virtual ~document(); - litehtml::document_container* container() { return m_container; } - uint_ptr get_font(const tchar_t* name, int size, const tchar_t* weight, const tchar_t* style, const tchar_t* decoration, font_metrics* fm); + document_container* container() { return m_container; } + uint_ptr get_font(const char* name, int size, const char* weight, const char* style, const char* decoration, font_metrics* fm); int render(int max_width, render_type rt = render_all); void draw(uint_ptr hdc, int x, int y, const position* clip); web_color get_def_color() { return m_def_color; } - int cvt_units(const tchar_t* str, int fontSize, bool* is_percent = 0) const; - int cvt_units(css_length& val, int fontSize, int size = 0) const; + int to_pixels(const char* str, int fontSize, bool* is_percent = nullptr) const; + void cvt_units(css_length& val, int fontSize, int size = 0) const; + int to_pixels(const css_length& val, int fontSize, int size = 0) const; int width() const; int height() const; - void add_stylesheet(const tchar_t* str, const tchar_t* baseurl, const tchar_t* media); + int content_width() const; + int content_height() const; + void add_stylesheet(const char* str, const char* baseurl, const char* media); bool on_mouse_over(int x, int y, int client_x, int client_y, position::vector& redraw_boxes); bool on_lbutton_down(int x, int y, int client_x, int client_y, position::vector& redraw_boxes); bool on_lbutton_up(int x, int y, int client_x, int client_y, position::vector& redraw_boxes); bool on_mouse_leave(position::vector& redraw_boxes); - litehtml::element::ptr create_element(const tchar_t* tag_name, const string_map& attributes); + element::ptr create_element(const char* tag_name, const string_map& attributes); element::ptr root(); const element::ptr over_element() const; void get_fixed_boxes(position::vector& fixed_boxes); void add_fixed_box(const position& pos); - void add_media_list(media_query_list::ptr list); + void add_media_list(const media_query_list::ptr& list); bool media_changed(); bool lang_changed(); - bool match_lang(const tstring & lang); - void add_tabular(const element::ptr& el); - const element::const_ptr get_over_element() const { return m_over_element; } + bool match_lang(const string& lang); + void add_tabular(const std::shared_ptr<render_item>& el); + element::const_ptr get_over_element() const { return m_over_element; } - void append_children_from_string(element& parent, const tchar_t* str); - void append_children_from_utf8(element& parent, const char* str); + void append_children_from_string(element& parent, const char* str); + void dump(dumper& cout); - static litehtml::document::ptr createFromString(const tchar_t* str, litehtml::document_container* objPainter, litehtml::context* ctx, litehtml::css* user_styles = 0); - static litehtml::document::ptr createFromUTF8(const char* str, litehtml::document_container* objPainter, litehtml::context* ctx, litehtml::css* user_styles = 0); + static litehtml::document::ptr createFromString(const char* str, litehtml::document_container* objPainter, const char* master_styles = litehtml::master_css, const char* user_styles = ""); private: - litehtml::uint_ptr add_font(const tchar_t* name, int size, const tchar_t* weight, const tchar_t* style, const tchar_t* decoration, font_metrics* fm); + uint_ptr add_font(const char* name, int size, const char* weight, const char* style, const char* decoration, font_metrics* fm); void create_node(void* gnode, elements_vector& elements, bool parseTextNode); bool update_media_lists(const media_features& features); void fix_tables_layout(); - void fix_table_children(element::ptr& el_ptr, style_display disp, const tchar_t* disp_str); - void fix_table_parent(element::ptr& el_ptr, style_display disp, const tchar_t* disp_str); + void fix_table_children(const std::shared_ptr<render_item>& el_ptr, style_display disp, const char* disp_str); + void fix_table_parent(const std::shared_ptr<render_item> & el_ptr, style_display disp, const char* disp_str); }; inline element::ptr document::root() @@ -123,11 +126,11 @@ namespace litehtml { return m_over_element; } - inline void document::add_tabular(const element::ptr& el) + inline void document::add_tabular(const std::shared_ptr<render_item>& el) { m_tabular_elements.push_back(el); } - inline bool document::match_lang(const tstring & lang) + inline bool document::match_lang(const string& lang) { return lang == m_lang || lang == m_culture; } diff --git a/src/plugins/litehtml_viewer/litehtml/document_container.cpp b/src/plugins/litehtml_viewer/litehtml/document_container.cpp @@ -0,0 +1,44 @@ +#include "html.h" +#include "document_container.h" + +void litehtml::document_container::split_text(const char* text, const std::function<void(const char*)>& on_word, const std::function<void(const char*)>& on_space) +{ + std::wstring str; + std::wstring str_in = (const wchar_t*)utf8_to_wchar(text); + ucode_t c; + for (size_t i = 0; i < str_in.length(); i++) + { + c = (ucode_t)str_in[i]; + if (c <= ' ' && (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f')) + { + if (!str.empty()) + { + on_word(wchar_to_utf8(str.c_str())); + str.clear(); + } + str += c; + on_space(wchar_to_utf8(str.c_str())); + str.clear(); + } + // CJK character range + else if (c >= 0x4E00 && c <= 0x9FCC) + { + if (!str.empty()) + { + on_word(wchar_to_utf8(str.c_str())); + str.clear(); + } + str += c; + on_word(wchar_to_utf8(str.c_str())); + str.clear(); + } + else + { + str += c; + } + } + if (!str.empty()) + { + on_word(wchar_to_utf8(str.c_str())); + } +} diff --git a/src/plugins/litehtml_viewer/litehtml/document_container.h b/src/plugins/litehtml_viewer/litehtml/document_container.h @@ -0,0 +1,71 @@ +#ifndef LH_DOCUMENT_CONTAINER_H +#define LH_DOCUMENT_CONTAINER_H + +#include "os_types.h" +#include "types.h" +#include "web_color.h" +#include "background.h" +#include "borders.h" +#include "element.h" +#include <memory> +#include <functional> + +namespace litehtml +{ + struct list_marker + { + string image; + const char* baseurl; + list_style_type marker_type; + web_color color; + position pos; + int index; + uint_ptr font; + }; + + // call back interface to draw text, images and other elements + class document_container + { + public: + virtual litehtml::uint_ptr create_font(const char* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm) = 0; + virtual void delete_font(litehtml::uint_ptr hFont) = 0; + virtual int text_width(const char* text, litehtml::uint_ptr hFont) = 0; + virtual void draw_text(litehtml::uint_ptr hdc, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos) = 0; + virtual int pt_to_px(int pt) const = 0; + virtual int get_default_font_size() const = 0; + virtual const char* get_default_font_name() const = 0; + virtual void draw_list_marker(litehtml::uint_ptr hdc, const litehtml::list_marker& marker) = 0; + virtual void load_image(const char* src, const char* baseurl, bool redraw_on_ready) = 0; + virtual void get_image_size(const char* src, const char* baseurl, litehtml::size& sz) = 0; + // Note: regular <img> images are also drawn with draw_background + // bg is guaranteed to have at least one item. + // backgrounds in bg are in CSS order - the last one is the farthest from the user. + // only the last background has valid background-color. + virtual void draw_background(litehtml::uint_ptr hdc, const std::vector<litehtml::background_paint>& bg) = 0; + virtual void draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root) = 0; + + virtual void set_caption(const char* caption) = 0; + virtual void set_base_url(const char* base_url) = 0; + virtual void link(const std::shared_ptr<litehtml::document>& doc, const litehtml::element::ptr& el) = 0; + virtual void on_anchor_click(const char* url, const litehtml::element::ptr& el) = 0; + virtual void set_cursor(const char* cursor) = 0; + virtual void transform_text(litehtml::string& text, litehtml::text_transform tt) = 0; + virtual void import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& baseurl) = 0; + virtual void set_clip(const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius) = 0; + virtual void del_clip() = 0; + virtual void get_client_rect(litehtml::position& client) const = 0; + virtual litehtml::element::ptr create_element( const char* tag_name, + const litehtml::string_map& attributes, + const std::shared_ptr<litehtml::document>& doc) = 0; + + virtual void get_media_features(litehtml::media_features& media) const = 0; + virtual void get_language(litehtml::string& language, litehtml::string& culture) const = 0; + virtual litehtml::string resolve_color(const litehtml::string& /*color*/) const { return litehtml::string(); } + virtual void split_text(const char* text, const std::function<void(const char*)>& on_word, const std::function<void(const char*)>& on_space); + + protected: + ~document_container() = default; + }; +} + +#endif // LH_DOCUMENT_CONTAINER_H diff --git a/src/plugins/litehtml_viewer/litehtml/el_anchor.cpp b/src/plugins/litehtml_viewer/litehtml/el_anchor.cpp @@ -6,14 +6,9 @@ litehtml::el_anchor::el_anchor(const std::shared_ptr<litehtml::document>& doc) : { } -litehtml::el_anchor::~el_anchor() -{ - -} - void litehtml::el_anchor::on_click() { - const tchar_t* href = get_attr(_t("href")); + const char* href = get_attr("href"); if(href) { @@ -23,9 +18,9 @@ void litehtml::el_anchor::on_click() void litehtml::el_anchor::apply_stylesheet( const litehtml::css& stylesheet ) { - if( get_attr(_t("href")) ) + if( get_attr("href") ) { - m_pseudo_classes.push_back(_t("link")); + m_pseudo_classes.push_back(_link_); } html_tag::apply_stylesheet(stylesheet); } diff --git a/src/plugins/litehtml_viewer/litehtml/el_anchor.h b/src/plugins/litehtml_viewer/litehtml/el_anchor.h @@ -8,11 +8,10 @@ namespace litehtml class el_anchor : public html_tag { public: - el_anchor(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_anchor(); + explicit el_anchor(const std::shared_ptr<litehtml::document>& doc); - virtual void on_click() override; - virtual void apply_stylesheet(const litehtml::css& stylesheet) override; + void on_click() override; + void apply_stylesheet(const litehtml::css& stylesheet) override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_base.cpp b/src/plugins/litehtml_viewer/litehtml/el_base.cpp @@ -2,17 +2,12 @@ #include "el_base.h" #include "document.h" -litehtml::el_base::el_base(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) +litehtml::el_base::el_base(const std::shared_ptr<document>& doc) : html_tag(doc) { } -litehtml::el_base::~el_base() -{ - -} - void litehtml::el_base::parse_attributes() { - get_document()->container()->set_base_url(get_attr(_t("href"))); + get_document()->container()->set_base_url(get_attr("href")); } diff --git a/src/plugins/litehtml_viewer/litehtml/el_base.h b/src/plugins/litehtml_viewer/litehtml/el_base.h @@ -8,10 +8,9 @@ namespace litehtml class el_base : public html_tag { public: - el_base(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_base(); + explicit el_base(const std::shared_ptr<litehtml::document>& doc); - virtual void parse_attributes() override; + void parse_attributes() override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_before_after.cpp b/src/plugins/litehtml_viewer/litehtml/el_before_after.cpp @@ -3,90 +3,104 @@ #include "el_text.h" #include "el_space.h" #include "el_image.h" +#include "utf8_strings.h" -litehtml::el_before_after_base::el_before_after_base(const std::shared_ptr<litehtml::document>& doc, bool before) : html_tag(doc) +litehtml::el_before_after_base::el_before_after_base(const std::shared_ptr<document>& doc, bool before) : html_tag(doc) { - if(before) - { - set_tagName(_t("::before")); - } else - { - set_tagName(_t("::after")); - } + m_tag = before ? __tag_before_ : __tag_after_; } -litehtml::el_before_after_base::~el_before_after_base() +void litehtml::el_before_after_base::add_style(const style& style) { + html_tag::add_style(style); -} - -void litehtml::el_before_after_base::add_style(const litehtml::style& st) -{ - html_tag::add_style(st); + auto children = m_children; + m_children.clear(); - tstring content = get_style_property(_t("content"), false, _t("")); - if(!content.empty()) + const auto& content_property = style.get_property(_content_); + if(content_property.m_type == prop_type_string && !content_property.m_string.empty()) { - int idx = value_index(content.c_str(), content_property_string); + int idx = value_index(content_property.m_string, content_property_string); if(idx < 0) { - tstring fnc; - tstring::size_type i = 0; - while(i < content.length() && i != tstring::npos) + string fnc; + string::size_type i = 0; + while(i < content_property.m_string.length() && i != string::npos) { - if(content.at(i) == _t('"')) + if(content_property.m_string.at(i) == '"' || content_property.m_string.at(i) == '\'') { + auto chr = content_property.m_string.at(i); fnc.clear(); i++; - tstring::size_type pos = content.find(_t('"'), i); - tstring txt; - if(pos == tstring::npos) + string::size_type pos = content_property.m_string.find(chr, i); + string txt; + if(pos == string::npos) { - txt = content.substr(i); - i = tstring::npos; + txt = content_property.m_string.substr(i); + i = string::npos; } else { - txt = content.substr(i, pos - i); + txt = content_property.m_string.substr(i, pos - i); i = pos + 1; } add_text(txt); - } else if(content.at(i) == _t('(')) + } else if(content_property.m_string.at(i) == '(') { i++; litehtml::trim(fnc); litehtml::lcase(fnc); - tstring::size_type pos = content.find(_t(')'), i); - tstring params; - if(pos == tstring::npos) + string::size_type pos = content_property.m_string.find(')', i); + string params; + if(pos == string::npos) { - params = content.substr(i); - i = tstring::npos; + params = content_property.m_string.substr(i); + i = string::npos; } else { - params = content.substr(i, pos - i); + params = content_property.m_string.substr(i, pos - i); i = pos + 1; } add_function(fnc, params); fnc.clear(); } else { - fnc += content.at(i); + fnc += content_property.m_string.at(i); i++; } } } } + + if(m_children.empty()) + { + m_children = children; + } } -void litehtml::el_before_after_base::add_text( const tstring& txt ) +void litehtml::el_before_after_base::add_text( const string& txt ) { - tstring word; - tstring esc; - for(tstring::size_type i = 0; i < txt.length(); i++) + string word; + string esc; + + for(auto chr : txt) { - if( (txt.at(i) == _t(' ')) || (txt.at(i) == _t('\t')) || (txt.at(i) == _t('\\') && !esc.empty()) ) + if(chr == '\\' || + !esc.empty() && esc.length() < 5 && (chr >= '0' && chr <= '9' || chr >= 'A' && chr <= 'Z' || chr >= 'a' && chr <= 'z')) + { + if(!esc.empty() && chr == '\\') + { + word += convert_escape(esc.c_str() + 1); + esc.clear(); + } + esc += chr; + } else { - if(esc.empty()) + if(!esc.empty()) + { + word += convert_escape(esc.c_str() + 1); + esc.clear(); + } + if(isspace(chr)) { if(!word.empty()) { @@ -94,26 +108,13 @@ void litehtml::el_before_after_base::add_text( const tstring& txt ) appendChild(el); word.clear(); } - - element::ptr el = std::make_shared<el_space>(txt.substr(i, 1).c_str(), get_document()); + word += chr; + element::ptr el = std::make_shared<el_text>(word.c_str(), get_document()); appendChild(el); + word.clear(); } else { - word += convert_escape(esc.c_str() + 1); - esc.clear(); - if(txt.at(i) == _t('\\')) - { - esc += txt.at(i); - } - } - } else - { - if(!esc.empty() || txt.at(i) == _t('\\')) - { - esc += txt.at(i); - } else - { - word += txt.at(i); + word += chr; } } } @@ -130,21 +131,21 @@ void litehtml::el_before_after_base::add_text( const tstring& txt ) } } -void litehtml::el_before_after_base::add_function( const tstring& fnc, const tstring& params ) +void litehtml::el_before_after_base::add_function( const string& fnc, const string& params ) { - int idx = value_index(fnc.c_str(), _t("attr;counter;url")); + int idx = value_index(fnc, "attr;counter;url"); switch(idx) { // attr case 0: { - tstring p_name = params; + string p_name = params; trim(p_name); lcase(p_name); element::ptr el_parent = parent(); if (el_parent) { - const tchar_t* attr_value = el_parent->get_attr(p_name.c_str()); + const char* attr_value = el_parent->get_attr(p_name.c_str()); if (attr_value) { add_text(attr_value); @@ -158,18 +159,18 @@ void litehtml::el_before_after_base::add_function( const tstring& fnc, const tst // url case 2: { - tstring p_url = params; + string p_url = params; trim(p_url); if(!p_url.empty()) { - if(p_url.at(0) == _t('\'') || p_url.at(0) == _t('\"')) + if(p_url.at(0) == '\'' || p_url.at(0) == '\"') { p_url.erase(0, 1); } } if(!p_url.empty()) { - if(p_url.at(p_url.length() - 1) == _t('\'') || p_url.at(p_url.length() - 1) == _t('\"')) + if(p_url.at(p_url.length() - 1) == '\'' || p_url.at(p_url.length() - 1) == '\"') { p_url.erase(p_url.length() - 1, 1); } @@ -177,9 +178,9 @@ void litehtml::el_before_after_base::add_function( const tstring& fnc, const tst if(!p_url.empty()) { element::ptr el = std::make_shared<el_image>(get_document()); - el->set_attr(_t("src"), p_url.c_str()); - el->set_attr(_t("style"), _t("display:inline-block")); - el->set_tagName(_t("img")); + el->set_attr("src", p_url.c_str()); + el->set_attr("style", "display:inline-block"); + el->set_tagName("img"); appendChild(el); el->parse_attributes(); } @@ -188,10 +189,13 @@ void litehtml::el_before_after_base::add_function( const tstring& fnc, const tst } } -litehtml::tchar_t litehtml::el_before_after_base::convert_escape( const tchar_t* txt ) +litehtml::string litehtml::el_before_after_base::convert_escape( const char* txt ) { - tchar_t* sss = 0; - return (tchar_t) t_strtol(txt, &sss, 16); + char* str_end; + wchar_t u_str[2]; + u_str[0] = (wchar_t) strtol(txt, &str_end, 16); + u_str[1] = 0; + return litehtml::string(litehtml_from_wchar(u_str)); } void litehtml::el_before_after_base::apply_stylesheet( const litehtml::css& stylesheet ) diff --git a/src/plugins/litehtml_viewer/litehtml/el_before_after.h b/src/plugins/litehtml_viewer/litehtml/el_before_after.h @@ -8,21 +8,20 @@ namespace litehtml class el_before_after_base : public html_tag { public: - el_before_after_base(const std::shared_ptr<litehtml::document>& doc, bool before); - virtual ~el_before_after_base(); + el_before_after_base(const std::shared_ptr<document>& doc, bool before); - virtual void add_style(const litehtml::style& st) override; - virtual void apply_stylesheet(const litehtml::css& stylesheet) override; + void add_style(const style& style) override; + void apply_stylesheet(const litehtml::css& stylesheet) override; private: - void add_text(const tstring& txt); - void add_function(const tstring& fnc, const tstring& params); - tchar_t convert_escape(const tchar_t* txt); + void add_text(const string& txt); + void add_function(const string& fnc, const string& params); + static string convert_escape(const char* txt); }; class el_before : public el_before_after_base { public: - el_before(const std::shared_ptr<litehtml::document>& doc) : el_before_after_base(doc, true) + explicit el_before(const std::shared_ptr<document>& doc) : el_before_after_base(doc, true) { } @@ -31,7 +30,7 @@ namespace litehtml class el_after : public el_before_after_base { public: - el_after(const std::shared_ptr<litehtml::document>& doc) : el_before_after_base(doc, false) + explicit el_after(const std::shared_ptr<document>& doc) : el_before_after_base(doc, false) { } diff --git a/src/plugins/litehtml_viewer/litehtml/el_body.cpp b/src/plugins/litehtml_viewer/litehtml/el_body.cpp @@ -6,11 +6,6 @@ litehtml::el_body::el_body(const std::shared_ptr<litehtml::document>& doc) : lit { } -litehtml::el_body::~el_body() -{ - -} - bool litehtml::el_body::is_body() const { return true; diff --git a/src/plugins/litehtml_viewer/litehtml/el_body.h b/src/plugins/litehtml_viewer/litehtml/el_body.h @@ -8,10 +8,9 @@ namespace litehtml class el_body : public html_tag { public: - el_body(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_body(); + explicit el_body(const std::shared_ptr<litehtml::document>& doc); - virtual bool is_body() const override; + bool is_body() const override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_break.cpp b/src/plugins/litehtml_viewer/litehtml/el_break.cpp @@ -6,11 +6,6 @@ litehtml::el_break::el_break(const std::shared_ptr<litehtml::document>& doc) : h } -litehtml::el_break::~el_break() -{ - -} - bool litehtml::el_break::is_break() const { return true; diff --git a/src/plugins/litehtml_viewer/litehtml/el_break.h b/src/plugins/litehtml_viewer/litehtml/el_break.h @@ -8,11 +8,10 @@ namespace litehtml class el_break : public html_tag { public: - el_break(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_break(); + explicit el_break(const std::shared_ptr<litehtml::document>& doc); - virtual bool is_break() const override; - }; + bool is_break() const override; + }; } #endif // LH_EL_BREAK_H diff --git a/src/plugins/litehtml_viewer/litehtml/el_cdata.cpp b/src/plugins/litehtml_viewer/litehtml/el_cdata.cpp @@ -1,22 +1,17 @@ #include "html.h" #include "el_cdata.h" -litehtml::el_cdata::el_cdata(const std::shared_ptr<litehtml::document>& doc) : litehtml::element(doc) +litehtml::el_cdata::el_cdata(const std::shared_ptr<document>& doc) : element(doc) { - m_skip = true; + //m_skip = true; } -litehtml::el_cdata::~el_cdata() -{ - -} - -void litehtml::el_cdata::get_text( tstring& text ) +void litehtml::el_cdata::get_text( string& text ) { text += m_text; } -void litehtml::el_cdata::set_data( const tchar_t* data ) +void litehtml::el_cdata::set_data( const char* data ) { if(data) { diff --git a/src/plugins/litehtml_viewer/litehtml/el_cdata.h b/src/plugins/litehtml_viewer/litehtml/el_cdata.h @@ -7,13 +7,12 @@ namespace litehtml { class el_cdata : public element { - tstring m_text; + string m_text; public: - el_cdata(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_cdata(); + explicit el_cdata(const std::shared_ptr<document>& doc); - virtual void get_text(tstring& text) override; - virtual void set_data(const tchar_t* data) override; + void get_text(string& text) override; + void set_data(const char* data) override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_comment.cpp b/src/plugins/litehtml_viewer/litehtml/el_comment.cpp @@ -1,22 +1,22 @@ #include "html.h" #include "el_comment.h" -litehtml::el_comment::el_comment(const std::shared_ptr<litehtml::document>& doc) : litehtml::element(doc) +litehtml::el_comment::el_comment(const std::shared_ptr<document>& doc) : element(doc) { - m_skip = true; + //m_skip = true; } -litehtml::el_comment::~el_comment() +bool litehtml::el_comment::is_comment() const { - + return true; } -void litehtml::el_comment::get_text( tstring& text ) +void litehtml::el_comment::get_text( string& text ) { text += m_text; } -void litehtml::el_comment::set_data( const tchar_t* data ) +void litehtml::el_comment::set_data( const char* data ) { if(data) { diff --git a/src/plugins/litehtml_viewer/litehtml/el_comment.h b/src/plugins/litehtml_viewer/litehtml/el_comment.h @@ -7,13 +7,18 @@ namespace litehtml { class el_comment : public element { - tstring m_text; + string m_text; public: - el_comment(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_comment(); + explicit el_comment(const std::shared_ptr<document>& doc); - virtual void get_text(tstring& text) override; - virtual void set_data(const tchar_t* data) override; + bool is_comment() const override; + void get_text(string& text) override; + void set_data(const char* data) override; + std::shared_ptr<render_item> create_render_item(const std::shared_ptr<render_item>& parent_ri) override + { + // Comments are not rendered + return nullptr; + } }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_div.cpp b/src/plugins/litehtml_viewer/litehtml/el_div.cpp @@ -2,22 +2,17 @@ #include "el_div.h" -litehtml::el_div::el_div(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) -{ - -} - -litehtml::el_div::~el_div() +litehtml::el_div::el_div(const document::ptr& doc) : html_tag(doc) { } void litehtml::el_div::parse_attributes() { - const tchar_t* str = get_attr(_t("align")); + const char* str = get_attr("align"); if(str) { - m_style.add_property(_t("text-align"), str, 0, false); + m_style.add_property(_text_align_, str); } html_tag::parse_attributes(); } diff --git a/src/plugins/litehtml_viewer/litehtml/el_div.h b/src/plugins/litehtml_viewer/litehtml/el_div.h @@ -8,10 +8,9 @@ namespace litehtml class el_div : public html_tag { public: - el_div(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_div(); + explicit el_div(const std::shared_ptr<litehtml::document>& doc); - virtual void parse_attributes() override; + void parse_attributes() override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_font.cpp b/src/plugins/litehtml_viewer/litehtml/el_font.cpp @@ -2,55 +2,52 @@ #include "el_font.h" -litehtml::el_font::el_font(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) -{ - -} - -litehtml::el_font::~el_font() +litehtml::el_font::el_font(const std::shared_ptr<document>& doc) : html_tag(doc) { } void litehtml::el_font::parse_attributes() { - const tchar_t* str = get_attr(_t("color")); + const char* str = get_attr("color"); if(str) { - m_style.add_property(_t("color"), str, 0, false); + m_style.add_property(_color_, str, "", false, get_document()->container()); } - str = get_attr(_t("face")); + str = get_attr("face"); if(str) { - m_style.add_property(_t("font-face"), str, 0, false); + m_style.add_property(_font_family_, str); } - str = get_attr(_t("size")); + str = get_attr("size"); if(str) { - int sz = t_atoi(str); + int sz = atoi(str); + if(*str == '+' || *str == '-') sz = 3 + sz; // relative size + if(sz <= 1) { - m_style.add_property(_t("font-size"), _t("x-small"), 0, false); + m_style.add_property(_font_size_, "x-small"); } else if(sz >= 6) { - m_style.add_property(_t("font-size"), _t("xx-large"), 0, false); + m_style.add_property(_font_size_, "xx-large"); } else { switch(sz) { case 2: - m_style.add_property(_t("font-size"), _t("small"), 0, false); + m_style.add_property(_font_size_, "small"); break; case 3: - m_style.add_property(_t("font-size"), _t("medium"), 0, false); + m_style.add_property(_font_size_, "medium"); break; case 4: - m_style.add_property(_t("font-size"), _t("large"), 0, false); + m_style.add_property(_font_size_, "large"); break; case 5: - m_style.add_property(_t("font-size"), _t("x-large"), 0, false); + m_style.add_property(_font_size_, "x-large"); break; } } diff --git a/src/plugins/litehtml_viewer/litehtml/el_font.h b/src/plugins/litehtml_viewer/litehtml/el_font.h @@ -8,10 +8,9 @@ namespace litehtml class el_font : public html_tag { public: - el_font(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_font(); + explicit el_font(const std::shared_ptr<litehtml::document>& doc); - virtual void parse_attributes() override; + void parse_attributes() override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_image.cpp b/src/plugins/litehtml_viewer/litehtml/el_image.cpp @@ -1,15 +1,10 @@ #include "html.h" #include "el_image.h" -#include "document.h" +#include "render_item.h" -litehtml::el_image::el_image(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) +litehtml::el_image::el_image(const document::ptr& doc) : html_tag(doc) { - m_display = display_inline_block; -} - -litehtml::el_image::~el_image( void ) -{ - + m_css.set_display(display_inline_block); } void litehtml::el_image::get_content_size( size& sz, int max_width ) @@ -17,196 +12,36 @@ void litehtml::el_image::get_content_size( size& sz, int max_width ) get_document()->container()->get_image_size(m_src.c_str(), 0, sz); } -int litehtml::el_image::calc_max_height(int image_height) -{ - document::ptr doc = get_document(); - int percentSize = 0; - if (m_css_max_height.units() == css_units_percentage) - { - auto el_parent = parent(); - if (el_parent) - { - if (!el_parent->get_predefined_height(percentSize)) - { - return image_height; - } - } - } - return doc->cvt_units(m_css_max_height, m_font_size, percentSize); -} - -int litehtml::el_image::line_height() const -{ - return height(); -} - bool litehtml::el_image::is_replaced() const { return true; } -int litehtml::el_image::render( int x, int y, int max_width, bool second_pass ) -{ - int parent_width = max_width; - - calc_outlines(parent_width); - - m_pos.move_to(x, y); - - document::ptr doc = get_document(); - - litehtml::size sz; - doc->container()->get_image_size(m_src.c_str(), 0, sz); - - m_pos.width = sz.width; - m_pos.height = sz.height; - - if(m_css_height.is_predefined() && m_css_width.is_predefined()) - { - m_pos.height = sz.height; - m_pos.width = sz.width; - - // check for max-width - if(!m_css_max_width.is_predefined()) - { - int max_width = doc->cvt_units(m_css_max_width, m_font_size, parent_width); - if(m_pos.width > max_width) - { - m_pos.width = max_width; - } - if(sz.width) - { - m_pos.height = (int) ((float) m_pos.width * (float) sz.height / (float)sz.width); - } else - { - m_pos.height = sz.height; - } - } - - // check for max-height - if(!m_css_max_height.is_predefined()) - { - int max_height = calc_max_height(sz.height); - if(m_pos.height > max_height) - { - m_pos.height = max_height; - } - if(sz.height) - { - m_pos.width = (int) (m_pos.height * (float)sz.width / (float)sz.height); - } else - { - m_pos.width = sz.width; - } - } - } else if(!m_css_height.is_predefined() && m_css_width.is_predefined()) - { - if (!get_predefined_height(m_pos.height)) - { - m_pos.height = (int)m_css_height.val(); - } - - // check for max-height - if(!m_css_max_height.is_predefined()) - { - int max_height = calc_max_height(sz.height); - if(m_pos.height > max_height) - { - m_pos.height = max_height; - } - } - - if(sz.height) - { - m_pos.width = (int) (m_pos.height * (float)sz.width / (float)sz.height); - } else - { - m_pos.width = sz.width; - } - } else if(m_css_height.is_predefined() && !m_css_width.is_predefined()) - { - m_pos.width = (int) m_css_width.calc_percent(parent_width); - - // check for max-width - if(!m_css_max_width.is_predefined()) - { - int max_width = doc->cvt_units(m_css_max_width, m_font_size, parent_width); - if(m_pos.width > max_width) - { - m_pos.width = max_width; - } - } - - if(sz.width) - { - m_pos.height = (int) ((float) m_pos.width * (float) sz.height / (float)sz.width); - } else - { - m_pos.height = sz.height; - } - } else - { - m_pos.width = (int) m_css_width.calc_percent(parent_width); - m_pos.height = 0; - if (!get_predefined_height(m_pos.height)) - { - m_pos.height = (int)m_css_height.val(); - } - - // check for max-height - if(!m_css_max_height.is_predefined()) - { - int max_height = calc_max_height(sz.height); - if(m_pos.height > max_height) - { - m_pos.height = max_height; - } - } - - // check for max-height - if(!m_css_max_width.is_predefined()) - { - int max_width = doc->cvt_units(m_css_max_width, m_font_size, parent_width); - if(m_pos.width > max_width) - { - m_pos.width = max_width; - } - } - } - - calc_auto_margins(parent_width); - - m_pos.x += content_margins_left(); - m_pos.y += content_margins_top(); - - return m_pos.width + content_margins_left() + content_margins_right(); -} - void litehtml::el_image::parse_attributes() { - m_src = get_attr(_t("src"), _t("")); + m_src = get_attr("src", ""); - const tchar_t* attr_height = get_attr(_t("height")); + const char* attr_height = get_attr("height"); if(attr_height) { - m_style.add_property(_t("height"), attr_height, 0, false); + m_style.add_property(_height_, attr_height); } - const tchar_t* attr_width = get_attr(_t("width")); + const char* attr_width = get_attr("width"); if(attr_width) { - m_style.add_property(_t("width"), attr_width, 0, false); + m_style.add_property(_width_, attr_width); } } -void litehtml::el_image::draw( uint_ptr hdc, int x, int y, const position* clip ) +void litehtml::el_image::draw(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) { - position pos = m_pos; + position pos = ri->pos(); pos.x += x; pos.y += y; position el_pos = pos; - el_pos += m_padding; - el_pos += m_borders; + el_pos += ri->get_paddings(); + el_pos += ri->get_borders(); // draw standard background here if (el_pos.does_intersect(clip)) @@ -214,8 +49,8 @@ void litehtml::el_image::draw( uint_ptr hdc, int x, int y, const position* clip const background* bg = get_background(); if (bg) { - background_paint bg_paint; - init_background_paint(pos, bg_paint, bg); + std::vector<background_paint> bg_paint; + init_background_paint(pos, bg_paint, bg, ri); get_document()->container()->draw_background(hdc, bg_paint); } @@ -230,15 +65,15 @@ void litehtml::el_image::draw( uint_ptr hdc, int x, int y, const position* clip bg.clip_box = pos; bg.origin_box = pos; bg.border_box = pos; - bg.border_box += m_padding; - bg.border_box += m_borders; + bg.border_box += ri->get_paddings(); + bg.border_box += ri->get_borders(); bg.repeat = background_repeat_no_repeat; bg.image_size.width = pos.width; bg.image_size.height = pos.height; - bg.border_radius = m_css_borders.radius.calc_percents(bg.border_box.width, bg.border_box.height); + bg.border_radius = css().get_borders().radius.calc_percents(bg.border_box.width, bg.border_box.height); bg.position_x = pos.x; bg.position_y = pos.y; - get_document()->container()->draw_background(hdc, bg); + get_document()->container()->draw_background(hdc, {bg}); } } @@ -246,28 +81,40 @@ void litehtml::el_image::draw( uint_ptr hdc, int x, int y, const position* clip if (el_pos.does_intersect(clip)) { position border_box = pos; - border_box += m_padding; - border_box += m_borders; + border_box += ri->get_paddings(); + border_box += ri->get_borders(); - borders bdr = m_css_borders; - bdr.radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height); + borders bdr = css().get_borders(); + bdr.radius = css().get_borders().radius.calc_percents(border_box.width, border_box.height); - get_document()->container()->draw_borders(hdc, bdr, border_box, have_parent() ? false : true); + get_document()->container()->draw_borders(hdc, bdr, border_box, is_root()); } } -void litehtml::el_image::parse_styles( bool is_reparse /*= false*/ ) +void litehtml::el_image::compute_styles(bool recursive) { - html_tag::parse_styles(is_reparse); + html_tag::compute_styles(recursive); if(!m_src.empty()) { - if(!m_css_height.is_predefined() && !m_css_width.is_predefined()) + if(!css().get_height().is_predefined() && !css().get_width().is_predefined()) { - get_document()->container()->load_image(m_src.c_str(), 0, true); + get_document()->container()->load_image(m_src.c_str(), nullptr, true); } else { - get_document()->container()->load_image(m_src.c_str(), 0, false); + get_document()->container()->load_image(m_src.c_str(), nullptr, false); } } } + +litehtml::string litehtml::el_image::dump_get_name() +{ + return "img src=\"" + m_src + "\""; +} + +std::shared_ptr<litehtml::render_item> litehtml::el_image::create_render_item(const std::shared_ptr<render_item>& parent_ri) +{ + auto ret = std::make_shared<render_item_image>(shared_from_this()); + ret->parent(parent_ri); + return ret; +} diff --git a/src/plugins/litehtml_viewer/litehtml/el_image.h b/src/plugins/litehtml_viewer/litehtml/el_image.h @@ -8,20 +8,21 @@ namespace litehtml class el_image : public html_tag { - tstring m_src; + string m_src; public: - el_image(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_image(void); + el_image(const document::ptr& doc); + + bool is_replaced() const override; + void parse_attributes() override; + void compute_styles(bool recursive = true) override; + void draw(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) override; + void get_content_size(size& sz, int max_width) override; + string dump_get_name() override; + + std::shared_ptr<render_item> create_render_item(const std::shared_ptr<render_item>& parent_ri) override; - virtual int line_height() const override; - virtual bool is_replaced() const override; - virtual int render(int x, int y, int max_width, bool second_pass = false) override; - virtual void parse_attributes() override; - virtual void parse_styles(bool is_reparse = false) override; - virtual void draw(uint_ptr hdc, int x, int y, const position* clip) override; - virtual void get_content_size(size& sz, int max_width) override; private: - int calc_max_height(int image_height); +// int calc_max_height(int image_height); }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_li.cpp b/src/plugins/litehtml_viewer/litehtml/el_li.cpp @@ -1,38 +0,0 @@ -#include "html.h" -#include "el_li.h" -#include "document.h" - -litehtml::el_li::el_li(const std::shared_ptr<litehtml::document>& doc) : litehtml::html_tag(doc) -{ -} - -litehtml::el_li::~el_li() -{ - -} - -int litehtml::el_li::render(int x, int y, int max_width, bool second_pass) -{ - if (m_list_style_type >= list_style_type_armenian && !m_index_initialized) - { - if (auto p = parent()) - { - tchar_t val[2] = { 1, 0 }; - for (int i = 0, n = (int)p->get_children_count(); i < n; ++i) - { - auto child = p->get_child(i); - if (child.get() == this) - { - set_attr(_t("list_index"), val); - break; - } - else if (!t_strcmp(child->get_tagName(), _t("li"))) - ++val[0]; - } - } - - m_index_initialized = true; - } - - return html_tag::render(x, y, max_width, second_pass); -} diff --git a/src/plugins/litehtml_viewer/litehtml/el_li.h b/src/plugins/litehtml_viewer/litehtml/el_li.h @@ -1,21 +0,0 @@ -#ifndef LH_EL_LI_H -#define LH_EL_LI_H - -#include "html_tag.h" - -namespace litehtml -{ - class el_li : public html_tag - { - public: - el_li(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_li(); - - virtual int render(int x, int y, int max_width, bool second_pass = false) override; - - private: - bool m_index_initialized = false; - }; -} - -#endif // LH_EL_LI_H diff --git a/src/plugins/litehtml_viewer/litehtml/el_link.cpp b/src/plugins/litehtml_viewer/litehtml/el_link.cpp @@ -3,12 +3,7 @@ #include "document.h" -litehtml::el_link::el_link(const std::shared_ptr<litehtml::document>& doc) : litehtml::html_tag(doc) -{ - -} - -litehtml::el_link::~el_link() +litehtml::el_link::el_link(const std::shared_ptr<document>& doc) : litehtml::html_tag(doc) { } @@ -19,15 +14,15 @@ void litehtml::el_link::parse_attributes() document::ptr doc = get_document(); - const tchar_t* rel = get_attr(_t("rel")); - if(rel && !t_strcmp(rel, _t("stylesheet"))) + const char* rel = get_attr("rel"); + if(rel && !strcmp(rel, "stylesheet")) { - const tchar_t* media = get_attr(_t("media")); - const tchar_t* href = get_attr(_t("href")); + const char* media = get_attr("media"); + const char* href = get_attr("href"); if(href && href[0]) { - tstring css_text; - tstring css_baseurl; + string css_text; + string css_baseurl; doc->container()->import_css(css_text, href, css_baseurl); if(!css_text.empty()) { diff --git a/src/plugins/litehtml_viewer/litehtml/el_link.h b/src/plugins/litehtml_viewer/litehtml/el_link.h @@ -8,11 +8,10 @@ namespace litehtml class el_link : public html_tag { public: - el_link(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_link(); + explicit el_link(const std::shared_ptr<litehtml::document>& doc); protected: - virtual void parse_attributes() override; + void parse_attributes() override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_para.cpp b/src/plugins/litehtml_viewer/litehtml/el_para.cpp @@ -2,21 +2,16 @@ #include "el_para.h" #include "document.h" -litehtml::el_para::el_para(const std::shared_ptr<litehtml::document>& doc) : litehtml::html_tag(doc) +litehtml::el_para::el_para(const std::shared_ptr<document>& doc) : html_tag(doc) { } -litehtml::el_para::~el_para() -{ - -} - void litehtml::el_para::parse_attributes() { - const tchar_t* str = get_attr(_t("align")); + const char* str = get_attr("align"); if(str) { - m_style.add_property(_t("text-align"), str, 0, false); + m_style.add_property(_text_align_, str); } html_tag::parse_attributes(); diff --git a/src/plugins/litehtml_viewer/litehtml/el_para.h b/src/plugins/litehtml_viewer/litehtml/el_para.h @@ -8,10 +8,9 @@ namespace litehtml class el_para : public html_tag { public: - el_para(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_para(); + explicit el_para(const std::shared_ptr<litehtml::document>& doc); - virtual void parse_attributes() override; + void parse_attributes() override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_script.cpp b/src/plugins/litehtml_viewer/litehtml/el_script.cpp @@ -3,12 +3,7 @@ #include "document.h" -litehtml::el_script::el_script(const std::shared_ptr<litehtml::document>& doc) : litehtml::element(doc) -{ - -} - -litehtml::el_script::~el_script() +litehtml::el_script::el_script(const std::shared_ptr<document>& doc) : element(doc) { } @@ -24,7 +19,12 @@ bool litehtml::el_script::appendChild(const ptr &el) return true; } -const litehtml::tchar_t* litehtml::el_script::get_tagName() const +litehtml::string_id litehtml::el_script::tag() const +{ + return _script_; +} + +const char* litehtml::el_script::get_tagName() const { - return _t("script"); + return "script"; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_script.h b/src/plugins/litehtml_viewer/litehtml/el_script.h @@ -7,14 +7,14 @@ namespace litehtml { class el_script : public element { - tstring m_text; + string m_text; public: - el_script(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_script(); + explicit el_script(const std::shared_ptr<document>& doc); - virtual void parse_attributes() override; - virtual bool appendChild(const ptr &el) override; - virtual const tchar_t* get_tagName() const override; + void parse_attributes() override; + bool appendChild(const ptr &el) override; + string_id tag() const override; + const char* get_tagName() const override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_space.cpp b/src/plugins/litehtml_viewer/litehtml/el_space.cpp @@ -2,18 +2,13 @@ #include "document.h" #include "el_space.h" -litehtml::el_space::el_space(const tchar_t* text, const std::shared_ptr<litehtml::document>& doc) : el_text(text, doc) +litehtml::el_space::el_space(const char* text, const std::shared_ptr<document>& doc) : el_text(text, doc) { } -litehtml::el_space::~el_space() -{ - -} - bool litehtml::el_space::is_white_space() const { - white_space ws = get_white_space(); + white_space ws = css().get_white_space(); if( ws == white_space_normal || ws == white_space_nowrap || ws == white_space_pre_line ) @@ -25,15 +20,25 @@ bool litehtml::el_space::is_white_space() const bool litehtml::el_space::is_break() const { - white_space ws = get_white_space(); + white_space ws = css().get_white_space(); if( ws == white_space_pre || ws == white_space_pre_line || ws == white_space_pre_wrap) { - if(m_text == _t("\n")) + if(m_text == "\n") { return true; } } return false; } + +bool litehtml::el_space::is_space() const +{ + return true; +} + +litehtml::string litehtml::el_space::dump_get_name() +{ + return "space: \"" + get_escaped_string(m_text) + "\""; +} diff --git a/src/plugins/litehtml_viewer/litehtml/el_space.h b/src/plugins/litehtml_viewer/litehtml/el_space.h @@ -9,11 +9,12 @@ namespace litehtml class el_space : public el_text { public: - el_space(const tchar_t* text, const std::shared_ptr<litehtml::document>& doc); - virtual ~el_space(); + el_space(const char* text, const std::shared_ptr<document>& doc); - virtual bool is_white_space() const override; - virtual bool is_break() const override; + bool is_white_space() const override; + bool is_break() const override; + bool is_space() const override; + string dump_get_name() override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_style.cpp b/src/plugins/litehtml_viewer/litehtml/el_style.cpp @@ -3,25 +3,20 @@ #include "document.h" -litehtml::el_style::el_style(const std::shared_ptr<litehtml::document>& doc) : litehtml::element(doc) -{ - -} - -litehtml::el_style::~el_style() +litehtml::el_style::el_style(const std::shared_ptr<document>& doc) : element(doc) { } void litehtml::el_style::parse_attributes() { - tstring text; + string text; for(auto& el : m_children) { el->get_text(text); } - get_document()->add_stylesheet( text.c_str(), 0, get_attr(_t("media")) ); + get_document()->add_stylesheet( text.c_str(), nullptr, get_attr("media") ); } bool litehtml::el_style::appendChild(const ptr &el) @@ -30,7 +25,12 @@ bool litehtml::el_style::appendChild(const ptr &el) return true; } -const litehtml::tchar_t* litehtml::el_style::get_tagName() const +litehtml::string_id litehtml::el_style::tag() const +{ + return _style_; +} + +const char* litehtml::el_style::get_tagName() const { - return _t("style"); + return "style"; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_style.h b/src/plugins/litehtml_viewer/litehtml/el_style.h @@ -9,12 +9,12 @@ namespace litehtml { elements_vector m_children; public: - el_style(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_style(); + explicit el_style(const std::shared_ptr<document>& doc); - virtual void parse_attributes() override; - virtual bool appendChild(const ptr &el) override; - virtual const tchar_t* get_tagName() const override; + void parse_attributes() override; + bool appendChild(const ptr &el) override; + string_id tag() const override; + const char* get_tagName() const override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_table.cpp b/src/plugins/litehtml_viewer/litehtml/el_table.cpp @@ -2,106 +2,70 @@ #include "el_table.h" #include "document.h" #include "iterators.h" -#include <algorithm> -litehtml::el_table::el_table(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) +litehtml::el_table::el_table(const std::shared_ptr<document>& doc) : html_tag(doc) { - m_border_spacing_x = 0; - m_border_spacing_y = 0; - m_border_collapse = border_collapse_separate; } -litehtml::el_table::~el_table() -{ - -} - -bool litehtml::el_table::appendChild(const litehtml::element::ptr& el) +bool litehtml::el_table::appendChild(const element::ptr& el) { if(!el) return false; - if(!t_strcmp(el->get_tagName(), _t("tbody")) || !t_strcmp(el->get_tagName(), _t("thead")) || !t_strcmp(el->get_tagName(), _t("tfoot"))) + if( el->tag() == _tbody_ || + el->tag() == _thead_ || + el->tag() == _tfoot_ || + el->tag() == _caption_) { return html_tag::appendChild(el); } return false; } -void litehtml::el_table::parse_styles(bool is_reparse) -{ - html_tag::parse_styles(is_reparse); - - m_border_collapse = (border_collapse) value_index(get_style_property(_t("border-collapse"), true, _t("separate")), border_collapse_strings, border_collapse_separate); - - if(m_border_collapse == border_collapse_separate) - { - m_css_border_spacing_x.fromString(get_style_property(_t("-litehtml-border-spacing-x"), true, _t("0px"))); - m_css_border_spacing_y.fromString(get_style_property(_t("-litehtml-border-spacing-y"), true, _t("0px"))); - - int fntsz = get_font_size(); - document::ptr doc = get_document(); - m_border_spacing_x = doc->cvt_units(m_css_border_spacing_x, fntsz); - m_border_spacing_y = doc->cvt_units(m_css_border_spacing_y, fntsz); - } else - { - m_border_spacing_x = 0; - m_border_spacing_y = 0; - m_padding.bottom = 0; - m_padding.top = 0; - m_padding.left = 0; - m_padding.right = 0; - m_css_padding.bottom.set_value(0, css_units_px); - m_css_padding.top.set_value(0, css_units_px); - m_css_padding.left.set_value(0, css_units_px); - m_css_padding.right.set_value(0, css_units_px); - } -} - void litehtml::el_table::parse_attributes() { - const tchar_t* str = get_attr(_t("width")); + const char* str = get_attr("width"); if(str) { - m_style.add_property(_t("width"), str, 0, false); + m_style.add_property(_width_, str); } - str = get_attr(_t("align")); + str = get_attr("align"); if(str) { - int align = value_index(str, _t("left;center;right")); + int align = value_index(str, "left;center;right"); switch(align) { case 1: - m_style.add_property(_t("margin-left"), _t("auto"), 0, false); - m_style.add_property(_t("margin-right"), _t("auto"), 0, false); + m_style.add_property(_margin_left_, "auto"); + m_style.add_property(_margin_right_, "auto"); break; case 2: - m_style.add_property(_t("margin-left"), _t("auto"), 0, false); - m_style.add_property(_t("margin-right"), _t("0"), 0, false); + m_style.add_property(_margin_left_, "auto"); + m_style.add_property(_margin_right_, "0"); break; } } - str = get_attr(_t("cellspacing")); + str = get_attr("cellspacing"); if(str) { - tstring val = str; - val += _t(" "); + string val = str; + val += " "; val += str; - m_style.add_property(_t("border-spacing"), val.c_str(), 0, false); + m_style.add_property(_border_spacing_, val); } - str = get_attr(_t("border")); + str = get_attr("border"); if(str) { - m_style.add_property(_t("border-width"), str, 0, false); + m_style.add_property(_border_width_, str); } - str = get_attr(_t("bgcolor")); + str = get_attr("bgcolor"); if (str) { - m_style.add_property(_t("background-color"), str, 0, false); + m_style.add_property(_background_color_, str, "", false, get_document()->container()); } html_tag::parse_attributes(); diff --git a/src/plugins/litehtml_viewer/litehtml/el_table.h b/src/plugins/litehtml_viewer/litehtml/el_table.h @@ -15,12 +15,10 @@ namespace litehtml class el_table : public html_tag { public: - el_table(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_table(); + explicit el_table(const std::shared_ptr<litehtml::document>& doc); - virtual bool appendChild(const litehtml::element::ptr& el) override; - virtual void parse_styles(bool is_reparse = false) override; - virtual void parse_attributes() override; + bool appendChild(const litehtml::element::ptr& el) override; + void parse_attributes() override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_td.cpp b/src/plugins/litehtml_viewer/litehtml/el_td.cpp @@ -2,48 +2,42 @@ #include "el_td.h" -litehtml::el_td::el_td(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) -{ - -} - -litehtml::el_td::~el_td() +litehtml::el_td::el_td(const std::shared_ptr<document>& doc) : html_tag(doc) { } void litehtml::el_td::parse_attributes() { - const tchar_t* str = get_attr(_t("width")); + const char* str = get_attr("width"); if(str) { - m_style.add_property(_t("width"), str, 0, false); + m_style.add_property(_width_, str); } - str = get_attr(_t("background")); + str = get_attr("background"); if(str) { - tstring url = _t("url('"); + string url = "url('"; url += str; - url += _t("')"); - m_style.add_property(_t("background-image"), url.c_str(), 0, false); + url += "')"; + m_style.add_property(_background_image_, url); } - str = get_attr(_t("align")); + str = get_attr("align"); if(str) { - m_style.add_property(_t("text-align"), str, 0, false); + m_style.add_property(_text_align_, str); } - str = get_attr(_t("bgcolor")); + str = get_attr("bgcolor"); if (str) { - m_style.add_property(_t("background-color"), str, 0, false); + m_style.add_property(_background_color_, str, "", false, get_document()->container()); } - str = get_attr(_t("valign")); + str = get_attr("valign"); if(str) { - m_style.add_property(_t("vertical-align"), str, 0, false); + m_style.add_property(_vertical_align_, str); } html_tag::parse_attributes(); } - diff --git a/src/plugins/litehtml_viewer/litehtml/el_td.h b/src/plugins/litehtml_viewer/litehtml/el_td.h @@ -8,10 +8,9 @@ namespace litehtml class el_td : public html_tag { public: - el_td(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_td(); + explicit el_td(const std::shared_ptr<litehtml::document>& doc); - virtual void parse_attributes() override; + void parse_attributes() override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_text.cpp b/src/plugins/litehtml_viewer/litehtml/el_text.cpp @@ -1,21 +1,16 @@ #include "html.h" #include "el_text.h" -#include "document.h" +#include "render_item.h" -litehtml::el_text::el_text(const tchar_t* text, const std::shared_ptr<litehtml::document>& doc) : element(doc) +litehtml::el_text::el_text(const char* text, const document::ptr& doc) : element(doc) { if(text) { m_text = text; } - m_text_transform = text_transform_none; m_use_transformed = false; m_draw_spaces = true; -} - -litehtml::el_text::~el_text() -{ - + css_w().set_display(display_inline_text); } void litehtml::el_text::get_content_size( size& sz, int max_width ) @@ -23,60 +18,77 @@ void litehtml::el_text::get_content_size( size& sz, int max_width ) sz = m_size; } -void litehtml::el_text::get_text( tstring& text ) +void litehtml::el_text::get_text( string& text ) { text += m_text; } -const litehtml::tchar_t* litehtml::el_text::get_style_property( const tchar_t* name, bool inherited, const tchar_t* def /*= 0*/ ) -{ - if(inherited) - { - element::ptr el_parent = parent(); - if (el_parent) - { - return el_parent->get_style_property(name, inherited, def); - } - } - return def; -} - -void litehtml::el_text::parse_styles(bool is_reparse) +void litehtml::el_text::compute_styles(bool recursive) { - m_text_transform = (text_transform) value_index(get_style_property(_t("text-transform"), true, _t("none")), text_transform_strings, text_transform_none); - if(m_text_transform != text_transform_none) + element::ptr el_parent = parent(); + if (el_parent) + { + css_w().set_line_height(el_parent->css().get_line_height()); + css_w().set_font(el_parent->css().get_font()); + css_w().set_font_metrics(el_parent->css().get_font_metrics()); + css_w().set_white_space(el_parent->css().get_white_space()); + css_w().set_text_transform(el_parent->css().get_text_transform()); + } + css_w().set_display(display_inline_text); + css_w().set_float(float_none); + + if(m_css.get_text_transform() != text_transform_none) { m_transformed_text = m_text; m_use_transformed = true; - get_document()->container()->transform_text(m_transformed_text, m_text_transform); + get_document()->container()->transform_text(m_transformed_text, m_css.get_text_transform()); + } else + { + m_use_transformed = false; } + element::ptr p = parent(); + while(p && p->css().get_display() == display_inline) + { + if(p->css().get_position() == element_position_relative) + { + css_w().set_offsets(p->css().get_offsets()); + css_w().set_position(element_position_relative); + break; + } + p = p->parent(); + } + if(p) + { + css_w().set_position(element_position_static); + } + if(is_white_space()) { - m_transformed_text = _t(" "); + m_transformed_text = " "; m_use_transformed = true; } else { - if(m_text == _t("\t")) + if(m_text == "\t") { - m_transformed_text = _t(" "); + m_transformed_text = " "; m_use_transformed = true; } - if(m_text == _t("\n") || m_text == _t("\r")) + if(m_text == "\n" || m_text == "\r") { - m_transformed_text = _t(""); + m_transformed_text = ""; m_use_transformed = true; } } font_metrics fm; uint_ptr font = 0; - element::ptr el_parent = parent(); if (el_parent) { - font = el_parent->get_font(&fm); + font = el_parent->css().get_font(); + fm = el_parent->css().get_font_metrics(); } - if(is_break()) + if(is_break() || !font) { m_size.height = 0; m_size.width = 0; @@ -88,24 +100,14 @@ void litehtml::el_text::parse_styles(bool is_reparse) m_draw_spaces = fm.draw_spaces; } -int litehtml::el_text::get_base_line() -{ - element::ptr el_parent = parent(); - if (el_parent) - { - return el_parent->get_base_line(); - } - return 0; -} - -void litehtml::el_text::draw( uint_ptr hdc, int x, int y, const position* clip ) +void litehtml::el_text::draw(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) { if(is_white_space() && !m_draw_spaces) { return; } - position pos = m_pos; + position pos = ri->pos(); pos.x += x; pos.y += y; @@ -116,73 +118,23 @@ void litehtml::el_text::draw( uint_ptr hdc, int x, int y, const position* clip ) { document::ptr doc = get_document(); - uint_ptr font = el_parent->get_font(); - litehtml::web_color color = el_parent->get_color(_t("color"), true, doc->get_def_color()); - doc->container()->draw_text(hdc, m_use_transformed ? m_transformed_text.c_str() : m_text.c_str(), font, color, pos); + uint_ptr font = el_parent->css().get_font(); + if(font) + { + web_color color = el_parent->css().get_color(); + doc->container()->draw_text(hdc, m_use_transformed ? m_transformed_text.c_str() : m_text.c_str(), font, + color, pos); + } } } } -int litehtml::el_text::line_height() const -{ - element::ptr el_parent = parent(); - if (el_parent) - { - return el_parent->line_height(); - } - return 0; -} - -litehtml::uint_ptr litehtml::el_text::get_font( font_metrics* fm /*= 0*/ ) -{ - element::ptr el_parent = parent(); - if (el_parent) - { - return el_parent->get_font(fm); - } - return 0; -} - -litehtml::style_display litehtml::el_text::get_display() const -{ - return display_inline_text; -} - -litehtml::white_space litehtml::el_text::get_white_space() const +litehtml::string litehtml::el_text::dump_get_name() { - element::ptr el_parent = parent(); - if (el_parent) return el_parent->get_white_space(); - return white_space_normal; + return "text: \"" + get_escaped_string(m_text) + "\""; } -litehtml::element_position litehtml::el_text::get_element_position(css_offsets* offsets) const +std::vector<std::tuple<litehtml::string, litehtml::string>> litehtml::el_text::dump_get_attrs() { - element::ptr p = parent(); - while(p && p->get_display() == display_inline) - { - if(p->get_element_position() == element_position_relative) - { - if(offsets) - { - *offsets = p->get_css_offsets(); - } - return element_position_relative; - } - p = p->parent(); - } - return element_position_static; -} - -litehtml::css_offsets litehtml::el_text::get_css_offsets() const -{ - element::ptr p = parent(); - while(p && p->get_display() == display_inline) - { - if(p->get_element_position() == element_position_relative) - { - return p->get_css_offsets(); - } - p = p->parent(); - } - return css_offsets(); + return std::vector<std::tuple<string, string>>(); } diff --git a/src/plugins/litehtml_viewer/litehtml/el_text.h b/src/plugins/litehtml_viewer/litehtml/el_text.h @@ -8,30 +8,23 @@ namespace litehtml class el_text : public element { protected: - tstring m_text; - tstring m_transformed_text; + string m_text; + string m_transformed_text; size m_size; - text_transform m_text_transform; bool m_use_transformed; bool m_draw_spaces; public: - el_text(const tchar_t* text, const std::shared_ptr<litehtml::document>& doc); - virtual ~el_text(); + el_text(const char* text, const document::ptr& doc); - virtual void get_text(tstring& text) override; - virtual const tchar_t* get_style_property(const tchar_t* name, bool inherited, const tchar_t* def = 0) override; - virtual void parse_styles(bool is_reparse) override; - virtual int get_base_line() override; - virtual void draw(uint_ptr hdc, int x, int y, const position* clip) override; - virtual int line_height() const override; - virtual uint_ptr get_font(font_metrics* fm = 0) override; - virtual style_display get_display() const override; - virtual white_space get_white_space() const override; - virtual element_position get_element_position(css_offsets* offsets = 0) const override; - virtual css_offsets get_css_offsets() const override; + void get_text(string& text) override; + void compute_styles(bool recursive) override; + bool is_text() const override { return true; } + void draw(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) override; + string dump_get_name() override; + std::vector<std::tuple<string, string>> dump_get_attrs() override; protected: - virtual void get_content_size(size& sz, int max_width) override; + void get_content_size(size& sz, int max_width) override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_title.cpp b/src/plugins/litehtml_viewer/litehtml/el_title.cpp @@ -2,19 +2,15 @@ #include "el_title.h" #include "document.h" -litehtml::el_title::el_title(const std::shared_ptr<litehtml::document>& doc) : litehtml::html_tag(doc) -{ - -} - -litehtml::el_title::~el_title() +litehtml::el_title::el_title(const std::shared_ptr<document>& doc) : html_tag(doc) { } void litehtml::el_title::parse_attributes() { - tstring text; + string text; get_text(text); get_document()->container()->set_caption(text.c_str()); } + diff --git a/src/plugins/litehtml_viewer/litehtml/el_title.h b/src/plugins/litehtml_viewer/litehtml/el_title.h @@ -8,11 +8,10 @@ namespace litehtml class el_title : public html_tag { public: - el_title(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_title(); + explicit el_title(const std::shared_ptr<litehtml::document>& doc); protected: - virtual void parse_attributes() override; + void parse_attributes() override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/el_tr.cpp b/src/plugins/litehtml_viewer/litehtml/el_tr.cpp @@ -2,50 +2,27 @@ #include "el_tr.h" -litehtml::el_tr::el_tr(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) -{ - -} - -litehtml::el_tr::~el_tr() +litehtml::el_tr::el_tr(const std::shared_ptr<document>& doc) : html_tag(doc) { } void litehtml::el_tr::parse_attributes() { - const tchar_t* str = get_attr(_t("align")); + const char* str = get_attr("align"); if(str) { - m_style.add_property(_t("text-align"), str, 0, false); + m_style.add_property(_text_align_, str); } - str = get_attr(_t("valign")); + str = get_attr("valign"); if(str) { - m_style.add_property(_t("vertical-align"), str, 0, false); + m_style.add_property(_vertical_align_, str); } - str = get_attr(_t("bgcolor")); + str = get_attr("bgcolor"); if (str) { - m_style.add_property(_t("background-color"), str, 0, false); + m_style.add_property(_background_color_, str, "", false, get_document()->container()); } html_tag::parse_attributes(); } - -void litehtml::el_tr::get_inline_boxes( position::vector& boxes ) -{ - position pos; - for(auto& el : m_children) - { - if(el->get_display() == display_table_cell) - { - pos.x = el->left() + el->margin_left(); - pos.y = el->top() - m_padding.top - m_borders.top; - - pos.width = el->right() - pos.x - el->margin_right() - el->margin_left(); - pos.height = el->height() + m_padding.top + m_padding.bottom + m_borders.top + m_borders.bottom; - - boxes.push_back(pos); - } - } -} diff --git a/src/plugins/litehtml_viewer/litehtml/el_tr.h b/src/plugins/litehtml_viewer/litehtml/el_tr.h @@ -8,11 +8,9 @@ namespace litehtml class el_tr : public html_tag { public: - el_tr(const std::shared_ptr<litehtml::document>& doc); - virtual ~el_tr(); + explicit el_tr(const std::shared_ptr<litehtml::document>& doc); - virtual void parse_attributes() override; - virtual void get_inline_boxes(position::vector& boxes) override; + void parse_attributes() override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/element.cpp b/src/plugins/litehtml_viewer/litehtml/element.cpp @@ -1,410 +1,330 @@ #include "html.h" #include "element.h" #include "document.h" +#include "render_item.h" +#include "el_before_after.h" + +namespace litehtml +{ #define LITEHTML_EMPTY_FUNC {} #define LITEHTML_RETURN_FUNC(ret) {return ret;} -litehtml::element::element(const std::shared_ptr<litehtml::document>& doc) : m_doc(doc) +element::element(const document::ptr& doc) : m_doc(doc) { - m_box = 0; - m_skip = false; } -litehtml::element::~element() +position element::get_placement() const { - -} - - -bool litehtml::element::is_point_inside( int x, int y ) -{ - if(get_display() != display_inline && get_display() != display_table_row) + position pos; + bool is_first = true; + for(const auto& ri_el : m_renders) { - position pos = m_pos; - pos += m_padding; - pos += m_borders; - if(pos.is_point_inside(x, y)) - { - return true; - } else + auto ri = ri_el.lock(); + if(ri) { - return false; - } - } else - { - position::vector boxes; - get_inline_boxes(boxes); - for(position::vector::iterator box = boxes.begin(); box != boxes.end(); box++) - { - if(box->is_point_inside(x, y)) + position ri_pos = ri_el.lock()->get_placement(); + if(is_first) { - return true; + is_first = false; + pos = ri_pos; + } else + { + if(pos.x < ri_pos.x) + { + pos.x = ri_pos.x; + } + if(pos.y < ri_pos.y) + { + pos.y = ri_pos.y; + } } } } - return false; + return pos; } -litehtml::web_color litehtml::element::get_color( const tchar_t* prop_name, bool inherited, const litehtml::web_color& def_color ) +bool element::is_inline_box() const { - const tchar_t* clrstr = get_style_property(prop_name, inherited, 0); - if(!clrstr) + if( css().get_display() == display_inline || + css().get_display() == display_inline_table || + css().get_display() == display_inline_block || + css().get_display() == display_inline_text || + css().get_display() == display_inline_flex) { - return def_color; + return true; } - return web_color::from_string(clrstr, get_document()->container()); + return false; } -litehtml::position litehtml::element::get_placement() const +bool element::is_ancestor(const ptr &el) const { - litehtml::position pos = m_pos; - element::ptr cur_el = parent(); - while(cur_el) + element::ptr el_parent = parent(); + while(el_parent && el_parent != el) { - pos.x += cur_el->m_pos.x; - pos.y += cur_el->m_pos.y; - cur_el = cur_el->parent(); + el_parent = el_parent->parent(); } - return pos; -} - -bool litehtml::element::is_inline_box() const -{ - style_display d = get_display(); - if( d == display_inline || - d == display_inline_table || - d == display_inline_block || - d == display_inline_text) + if(el_parent) { return true; } return false; } -bool litehtml::element::collapse_top_margin() const +bool element::is_table_skip() const { - if(!m_borders.top && !m_padding.top && in_normal_flow() && get_float() == float_none && m_margins.top >= 0 && have_parent()) - { - return true; - } - return false; + return is_space() || is_comment() || css().get_display() == display_none; } -bool litehtml::element::collapse_bottom_margin() const +string element::dump_get_name() { - if(!m_borders.bottom && !m_padding.bottom && in_normal_flow() && get_float() == float_none && m_margins.bottom >= 0 && have_parent()) - { - return true; - } - return false; + return "element"; } -bool litehtml::element::get_predefined_height(int& p_height) const +std::vector<std::tuple<string, string>> element::dump_get_attrs() { - css_length h = get_css_height(); - if(h.is_predefined()) - { - p_height = m_pos.height; - return false; - } - if(h.units() == css_units_percentage) - { - element::ptr el_parent = parent(); - if (!el_parent) - { - position client_pos; - get_document()->container()->get_client_rect(client_pos); - p_height = h.calc_percent(client_pos.height); - return true; - } else - { - int ph = 0; - if (el_parent->get_predefined_height(ph)) - { - p_height = h.calc_percent(ph); - if (is_body()) - { - p_height -= content_margins_height(); - } - return true; - } else - { - p_height = m_pos.height; - return false; - } - } - } - p_height = get_document()->cvt_units(h, get_font_size()); - return true; + return m_css.dump_get_attrs(); } -void litehtml::element::calc_document_size( litehtml::size& sz, int x /*= 0*/, int y /*= 0*/ ) +void element::dump(dumper& cout) { - if(is_visible()) + cout.begin_node(dump_get_name()); + + auto attrs = dump_get_attrs(); + if(!attrs.empty()) { - sz.width = std::max(sz.width, x + right()); - sz.height = std::max(sz.height, y + bottom()); + cout.begin_attrs_group("attributes"); + for (const auto &attr: attrs) + { + cout.add_attr(std::get<0>(attr), std::get<1>(attr)); + } + cout.end_attrs_group(); } -} -void litehtml::element::get_redraw_box(litehtml::position& pos, int x /*= 0*/, int y /*= 0*/) -{ - if(is_visible()) + if(!m_children.empty()) { - int p_left = std::min(pos.left(), x + m_pos.left() - m_padding.left - m_borders.left); - int p_right = std::max(pos.right(), x + m_pos.right() + m_padding.left + m_borders.left); - int p_top = std::min(pos.top(), y + m_pos.top() - m_padding.top - m_borders.top); - int p_bottom = std::max(pos.bottom(), y + m_pos.bottom() + m_padding.bottom + m_borders.bottom); - - pos.x = p_left; - pos.y = p_top; - pos.width = p_right - p_left; - pos.height = p_bottom - p_top; + cout.begin_attrs_group("children"); + for (const auto &el: m_children) + { + el->dump(cout); + } + cout.end_attrs_group(); } + + cout.end_node(); } -int litehtml::element::calc_width(int defVal) const +std::shared_ptr<render_item> element::create_render_item(const std::shared_ptr<render_item>& parent_ri) { - css_length w = get_css_width(); - if(w.is_predefined()) + std::shared_ptr<render_item> ret; + + if(css().get_display() == display_table_column || + css().get_display() == display_table_column_group || + css().get_display() == display_table_footer_group || + css().get_display() == display_table_header_group || + css().get_display() == display_table_row_group) + { + ret = std::make_shared<render_item_table_part>(shared_from_this()); + } else if(css().get_display() == display_table_row) { - return defVal; + ret = std::make_shared<render_item_table_row>(shared_from_this()); + } else if(css().get_display() == display_block || + css().get_display() == display_table_cell || + css().get_display() == display_table_caption || + css().get_display() == display_list_item || + css().get_display() == display_inline_block) + { + ret = std::make_shared<render_item_block>(shared_from_this()); + } else if(css().get_display() == display_table || css().get_display() == display_inline_table) + { + ret = std::make_shared<render_item_table>(shared_from_this()); + } else if(css().get_display() == display_inline || css().get_display() == display_inline_text) + { + ret = std::make_shared<render_item_inline>(shared_from_this()); + } else if(css().get_display() == display_flex || css().get_display() == display_inline_flex) + { + ret = std::make_shared<render_item_flex>(shared_from_this()); } - if(w.units() == css_units_percentage) + if(ret) { - element::ptr el_parent = parent(); - if (!el_parent) + if (css().get_display() == display_table || + css().get_display() == display_inline_table || + css().get_display() == display_table_caption || + css().get_display() == display_table_cell || + css().get_display() == display_table_column || + css().get_display() == display_table_column_group || + css().get_display() == display_table_footer_group || + css().get_display() == display_table_header_group || + css().get_display() == display_table_row || + css().get_display() == display_table_row_group) { - position client_pos; - get_document()->container()->get_client_rect(client_pos); - return w.calc_percent(client_pos.width); - } else + get_document()->add_tabular(ret); + } + + ret->parent(parent_ri); + for(const auto& el : m_children) { - int pw = el_parent->calc_width(defVal); - if (is_body()) + auto ri = el->create_render_item(ret); + if(ri) { - pw -= content_margins_width(); + ret->add_child(ri); } - return w.calc_percent(pw); } } - return get_document()->cvt_units(w, get_font_size()); -} - -bool litehtml::element::is_ancestor(const ptr &el) const -{ - element::ptr el_parent = parent(); - while(el_parent && el_parent != el) - { - el_parent = el_parent->parent(); - } - if(el_parent) - { - return true; - } - return false; + return ret; } -int litehtml::element::get_inline_shift_left() +bool element::requires_styles_update() { - int ret = 0; - element::ptr el_parent = parent(); - if (el_parent) + for (const auto& used_style : m_used_styles) { - if (el_parent->get_display() == display_inline) + if(used_style->m_selector->is_media_valid()) { - style_display disp = get_display(); - - if (disp == display_inline_text || disp == display_inline_block) + int res = select(*(used_style->m_selector), true); + if( (res == select_no_match && used_style->m_used) || (res == select_match && !used_style->m_used) ) { - element::ptr el = shared_from_this(); - while (el_parent && el_parent->get_display() == display_inline) - { - if (el_parent->is_first_child_inline(el)) - { - ret += el_parent->padding_left() + el_parent->border_left() + el_parent->margin_left(); - } - el = el_parent; - el_parent = el_parent->parent(); - } + return true; } } } + return false; +} - return ret; +void element::add_render(const std::shared_ptr<render_item>& ri) +{ + m_renders.push_back(ri); } -int litehtml::element::get_inline_shift_right() +bool element::find_styles_changes( position::vector& redraw_boxes) { - int ret = 0; - element::ptr el_parent = parent(); - if (el_parent) + if(css().get_display() == display_inline_text) { - if (el_parent->get_display() == display_inline) - { - style_display disp = get_display(); + return false; + } + + bool ret = false; - if (disp == display_inline_text || disp == display_inline_block) + if(requires_styles_update()) + { + auto fetch_boxes = [&](const std::shared_ptr<element>& el) { - element::ptr el = shared_from_this(); - while (el_parent && el_parent->get_display() == display_inline) + for(const auto& weak_ri : el->m_renders) { - if (el_parent->is_last_child_inline(el)) + auto ri = weak_ri.lock(); + if(ri) { - ret += el_parent->padding_right() + el_parent->border_right() + el_parent->margin_right(); + position::vector boxes; + ri->get_rendering_boxes(boxes); + for (auto &box: boxes) + { + redraw_boxes.push_back(box); + } } - el = el_parent; - el_parent = el_parent->parent(); } - } + }; + fetch_boxes(shared_from_this()); + for (auto& el : m_children) + { + fetch_boxes(el); } - } + refresh_styles(); + compute_styles(); + ret = true; + } + for (auto& el : m_children) + { + if(el->find_styles_changes(redraw_boxes)) + { + ret = true; + } + } return ret; } -void litehtml::element::apply_relative_shift(int parent_width) +element::ptr element::_add_before_after(int type, const style& style) { - css_offsets offsets; - if (get_element_position(&offsets) == element_position_relative) + if(style.get_property(_content_).m_type != prop_type_invalid) { - element::ptr parent_ptr = parent(); - if (!offsets.left.is_predefined()) + element::ptr el; + if(type == 0) { - m_pos.x += offsets.left.calc_percent(parent_width); - } - else if (!offsets.right.is_predefined()) + el = std::make_shared<el_before>(get_document()); + m_children.insert(m_children.begin(), el); + } else { - m_pos.x -= offsets.right.calc_percent(parent_width); + el = std::make_shared<el_after>(get_document()); + m_children.insert(m_children.end(), el); } - if (!offsets.top.is_predefined()) - { - int h = 0; - - if (offsets.top.units() == css_units_percentage) - { - element::ptr el_parent = parent(); - if (el_parent) - { - el_parent->get_predefined_height(h); - } - } + el->parent(shared_from_this()); + return el; + } + return nullptr; +} - m_pos.y += offsets.top.calc_percent(h); - } - else if (!offsets.bottom.is_predefined()) - { - int h = 0; - if (offsets.top.units() == css_units_percentage) - { - element::ptr el_parent = parent(); - if (el_parent) - { - el_parent->get_predefined_height(h); - } - } +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_vector& res) LITEHTML_EMPTY_FUNC +elements_vector element::select_all(const css_selector& selector) LITEHTML_RETURN_FUNC(elements_vector()) +elements_vector element::select_all(const string& selector) LITEHTML_RETURN_FUNC(elements_vector()) +element::ptr element::select_one( const css_selector& selector ) LITEHTML_RETURN_FUNC(nullptr) +element::ptr element::select_one( const string& selector ) LITEHTML_RETURN_FUNC(nullptr) +element::ptr element::find_adjacent_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/) LITEHTML_RETURN_FUNC(nullptr) +element::ptr element::find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/) LITEHTML_RETURN_FUNC(nullptr) +bool element::is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const LITEHTML_RETURN_FUNC(false) +bool element::is_nth_child(const element::ptr&, int num, int off, bool of_type) const LITEHTML_RETURN_FUNC(false) +bool element::is_only_child(const element::ptr& el, bool of_type) const LITEHTML_RETURN_FUNC(false) +element::ptr element::get_child( int idx ) const LITEHTML_RETURN_FUNC(nullptr) +size_t element::get_children_count() const LITEHTML_RETURN_FUNC(0) +void element::update_floats(int dy, const ptr &parent) LITEHTML_EMPTY_FUNC +bool element::is_floats_holder() const LITEHTML_RETURN_FUNC(false) +void element::get_content_size( size& sz, int max_width ) LITEHTML_EMPTY_FUNC +bool element::appendChild(const ptr &el) LITEHTML_RETURN_FUNC(false) +bool element::removeChild(const ptr &el) LITEHTML_RETURN_FUNC(false) +void element::clearRecursive() LITEHTML_EMPTY_FUNC +string_id element::id() const LITEHTML_RETURN_FUNC(empty_id) +string_id element::tag() const LITEHTML_RETURN_FUNC(empty_id) +const char* element::get_tagName() const LITEHTML_RETURN_FUNC("") +void element::set_tagName( const char* tag ) LITEHTML_EMPTY_FUNC +void element::set_data( const char* data ) LITEHTML_EMPTY_FUNC +void element::set_attr( const char* name, const char* val ) LITEHTML_EMPTY_FUNC +void element::apply_stylesheet( const litehtml::css& stylesheet ) LITEHTML_EMPTY_FUNC +void element::refresh_styles() LITEHTML_EMPTY_FUNC +void element::on_click() LITEHTML_EMPTY_FUNC +void element::compute_styles( bool recursive ) LITEHTML_EMPTY_FUNC +const char* element::get_attr( const char* name, const char* def /*= 0*/ ) const LITEHTML_RETURN_FUNC(def) +bool element::is_white_space() const LITEHTML_RETURN_FUNC(false) +bool element::is_space() const LITEHTML_RETURN_FUNC(false) +bool element::is_comment() const LITEHTML_RETURN_FUNC(false) +bool element::is_body() const LITEHTML_RETURN_FUNC(false) +bool element::is_break() const LITEHTML_RETURN_FUNC(false) +bool element::is_text() const LITEHTML_RETURN_FUNC(false) - m_pos.y -= offsets.bottom.calc_percent(h); - } - } -} +bool element::on_mouse_over() LITEHTML_RETURN_FUNC(false) +bool element::on_mouse_leave() LITEHTML_RETURN_FUNC(false) +bool element::on_lbutton_down() LITEHTML_RETURN_FUNC(false) +bool element::on_lbutton_up() LITEHTML_RETURN_FUNC(false) +bool element::set_pseudo_class( string_id cls, bool add ) LITEHTML_RETURN_FUNC(false) +bool element::set_class( const char* pclass, bool add ) LITEHTML_RETURN_FUNC(false) +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) +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("") +float element::get_number_property (string_id name, bool inherited, float defval, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC(0) +string_vector element::get_string_vector_property (string_id name, bool inherited, const string_vector& default_value, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC({}) +int_vector element::get_int_vector_property (string_id name, bool inherited, const int_vector& default_value, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC({}) +length_vector element::get_length_vector_property (string_id name, bool inherited, const length_vector& default_value, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC({}) +size_vector element::get_size_vector_property (string_id name, bool inherited, const size_vector& default_value, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC({}) +string element::get_custom_property (string_id name, const string& defval) const LITEHTML_RETURN_FUNC("") +void element::get_text( string& text ) LITEHTML_EMPTY_FUNC +void element::parse_attributes() LITEHTML_EMPTY_FUNC +int element::select(const string& selector) LITEHTML_RETURN_FUNC(select_no_match) +int element::select(const css_selector& selector, bool apply_pseudo) LITEHTML_RETURN_FUNC(select_no_match) +int element::select( const css_element_selector& selector, bool apply_pseudo /*= true*/ ) LITEHTML_RETURN_FUNC(select_no_match) +element::ptr element::find_ancestor(const css_selector& selector, bool apply_pseudo, bool* is_pseudo) LITEHTML_RETURN_FUNC(nullptr) -void litehtml::element::calc_auto_margins(int parent_width) LITEHTML_EMPTY_FUNC -const litehtml::background* litehtml::element::get_background(bool own_only) LITEHTML_RETURN_FUNC(0) -litehtml::element::ptr litehtml::element::get_element_by_point(int x, int y, int client_x, int client_y) LITEHTML_RETURN_FUNC(0) -litehtml::element::ptr litehtml::element::get_child_by_point(int x, int y, int client_x, int client_y, draw_flag flag, int zindex) LITEHTML_RETURN_FUNC(0) -void litehtml::element::get_line_left_right( int y, int def_right, int& ln_left, int& ln_right ) LITEHTML_EMPTY_FUNC -void litehtml::element::add_style( const litehtml::style& st ) LITEHTML_EMPTY_FUNC -void litehtml::element::select_all(const css_selector& selector, litehtml::elements_vector& res) LITEHTML_EMPTY_FUNC -litehtml::elements_vector litehtml::element::select_all(const litehtml::css_selector& selector) LITEHTML_RETURN_FUNC(litehtml::elements_vector()) -litehtml::elements_vector litehtml::element::select_all(const litehtml::tstring& selector) LITEHTML_RETURN_FUNC(litehtml::elements_vector()) -litehtml::element::ptr litehtml::element::select_one( const css_selector& selector ) LITEHTML_RETURN_FUNC(0) -litehtml::element::ptr litehtml::element::select_one( const tstring& selector ) LITEHTML_RETURN_FUNC(0) -litehtml::element::ptr litehtml::element::find_adjacent_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/) LITEHTML_RETURN_FUNC(0) -litehtml::element::ptr litehtml::element::find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/) LITEHTML_RETURN_FUNC(0) -bool litehtml::element::is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const LITEHTML_RETURN_FUNC(false) -bool litehtml::element::is_nth_child(const element::ptr&, int num, int off, bool of_type) const LITEHTML_RETURN_FUNC(false) -bool litehtml::element::is_only_child(const element::ptr& el, bool of_type) const LITEHTML_RETURN_FUNC(false) -litehtml::overflow litehtml::element::get_overflow() const LITEHTML_RETURN_FUNC(overflow_visible) -void litehtml::element::draw_children( uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex ) LITEHTML_EMPTY_FUNC -void litehtml::element::draw_stacking_context( uint_ptr hdc, int x, int y, const position* clip, bool with_positioned ) LITEHTML_EMPTY_FUNC -void litehtml::element::render_positioned(render_type rt) LITEHTML_EMPTY_FUNC -int litehtml::element::get_zindex() const LITEHTML_RETURN_FUNC(0) -bool litehtml::element::fetch_positioned() LITEHTML_RETURN_FUNC(false) -litehtml::visibility litehtml::element::get_visibility() const LITEHTML_RETURN_FUNC(visibility_visible) -void litehtml::element::apply_vertical_align() LITEHTML_EMPTY_FUNC -void litehtml::element::set_css_width( css_length& w ) LITEHTML_EMPTY_FUNC -litehtml::element::ptr litehtml::element::get_child( int idx ) const LITEHTML_RETURN_FUNC(0) -size_t litehtml::element::get_children_count() const LITEHTML_RETURN_FUNC(0) -void litehtml::element::calc_outlines( int parent_width ) LITEHTML_EMPTY_FUNC -litehtml::css_length litehtml::element::get_css_width() const LITEHTML_RETURN_FUNC(css_length()) -litehtml::css_length litehtml::element::get_css_height() const LITEHTML_RETURN_FUNC(css_length()) -litehtml::element_clear litehtml::element::get_clear() const LITEHTML_RETURN_FUNC(clear_none) -litehtml::css_length litehtml::element::get_css_left() const LITEHTML_RETURN_FUNC(css_length()) -litehtml::css_length litehtml::element::get_css_right() const LITEHTML_RETURN_FUNC(css_length()) -litehtml::css_length litehtml::element::get_css_top() const LITEHTML_RETURN_FUNC(css_length()) -litehtml::css_length litehtml::element::get_css_bottom() const LITEHTML_RETURN_FUNC(css_length()) -litehtml::css_offsets litehtml::element::get_css_offsets() const LITEHTML_RETURN_FUNC(css_offsets()) -litehtml::vertical_align litehtml::element::get_vertical_align() const LITEHTML_RETURN_FUNC(va_baseline) -int litehtml::element::place_element(const ptr &el, int max_width) LITEHTML_RETURN_FUNC(0) -int litehtml::element::render_inline(const ptr &container, int max_width) LITEHTML_RETURN_FUNC(0) -void litehtml::element::add_positioned(const ptr &el) LITEHTML_EMPTY_FUNC -int litehtml::element::find_next_line_top( int top, int width, int def_right ) LITEHTML_RETURN_FUNC(0) -litehtml::element_float litehtml::element::get_float() const LITEHTML_RETURN_FUNC(float_none) -void litehtml::element::add_float(const ptr &el, int x, int y) LITEHTML_EMPTY_FUNC -void litehtml::element::update_floats(int dy, const ptr &parent) LITEHTML_EMPTY_FUNC -int litehtml::element::get_line_left( int y ) LITEHTML_RETURN_FUNC(0) -int litehtml::element::get_line_right( int y, int def_right ) LITEHTML_RETURN_FUNC(def_right) -int litehtml::element::get_left_floats_height() const LITEHTML_RETURN_FUNC(0) -int litehtml::element::get_right_floats_height() const LITEHTML_RETURN_FUNC(0) -int litehtml::element::get_floats_height(element_float el_float) const LITEHTML_RETURN_FUNC(0) -bool litehtml::element::is_floats_holder() const LITEHTML_RETURN_FUNC(false) -void litehtml::element::get_content_size( size& sz, int max_width ) LITEHTML_EMPTY_FUNC -void litehtml::element::init() LITEHTML_EMPTY_FUNC -int litehtml::element::render( int x, int y, int max_width, bool second_pass ) LITEHTML_RETURN_FUNC(0) -bool litehtml::element::appendChild(const ptr &el) LITEHTML_RETURN_FUNC(false) -bool litehtml::element::removeChild(const ptr &el) LITEHTML_RETURN_FUNC(false) -void litehtml::element::clearRecursive() LITEHTML_EMPTY_FUNC -const litehtml::tchar_t* litehtml::element::get_tagName() const LITEHTML_RETURN_FUNC(_t("")) -void litehtml::element::set_tagName( const tchar_t* tag ) LITEHTML_EMPTY_FUNC -void litehtml::element::set_data( const tchar_t* data ) LITEHTML_EMPTY_FUNC -void litehtml::element::set_attr( const tchar_t* name, const tchar_t* val ) LITEHTML_EMPTY_FUNC -void litehtml::element::apply_stylesheet( const litehtml::css& stylesheet ) LITEHTML_EMPTY_FUNC -void litehtml::element::refresh_styles() LITEHTML_EMPTY_FUNC -void litehtml::element::on_click() LITEHTML_EMPTY_FUNC -void litehtml::element::init_font() LITEHTML_EMPTY_FUNC -void litehtml::element::get_inline_boxes( position::vector& boxes ) LITEHTML_EMPTY_FUNC -void litehtml::element::parse_styles( bool is_reparse /*= false*/ ) LITEHTML_EMPTY_FUNC -const litehtml::tchar_t* litehtml::element::get_attr( const tchar_t* name, const tchar_t* def /*= 0*/ ) const LITEHTML_RETURN_FUNC(def) -bool litehtml::element::is_white_space() const LITEHTML_RETURN_FUNC(false) -bool litehtml::element::is_body() const LITEHTML_RETURN_FUNC(false) -bool litehtml::element::is_break() const LITEHTML_RETURN_FUNC(false) -int litehtml::element::get_base_line() LITEHTML_RETURN_FUNC(0) -bool litehtml::element::on_mouse_over() LITEHTML_RETURN_FUNC(false) -bool litehtml::element::on_mouse_leave() LITEHTML_RETURN_FUNC(false) -bool litehtml::element::on_lbutton_down() LITEHTML_RETURN_FUNC(false) -bool litehtml::element::on_lbutton_up() LITEHTML_RETURN_FUNC(false) -bool litehtml::element::find_styles_changes( position::vector& redraw_boxes, int x, int y ) LITEHTML_RETURN_FUNC(false) -const litehtml::tchar_t* litehtml::element::get_cursor() LITEHTML_RETURN_FUNC(0) -litehtml::white_space litehtml::element::get_white_space() const LITEHTML_RETURN_FUNC(white_space_normal) -litehtml::style_display litehtml::element::get_display() const LITEHTML_RETURN_FUNC(display_none) -bool litehtml::element::set_pseudo_class( const tchar_t* pclass, bool add ) LITEHTML_RETURN_FUNC(false) -bool litehtml::element::set_class( const tchar_t* pclass, bool add ) LITEHTML_RETURN_FUNC(false) -litehtml::element_position litehtml::element::get_element_position(css_offsets* offsets) const LITEHTML_RETURN_FUNC(element_position_static) -bool litehtml::element::is_replaced() const LITEHTML_RETURN_FUNC(false) -int litehtml::element::line_height() const LITEHTML_RETURN_FUNC(0) -void litehtml::element::draw( uint_ptr hdc, int x, int y, const position* clip ) LITEHTML_EMPTY_FUNC -void litehtml::element::draw_background( uint_ptr hdc, int x, int y, const position* clip ) LITEHTML_EMPTY_FUNC -const litehtml::tchar_t* litehtml::element::get_style_property( const tchar_t* name, bool inherited, const tchar_t* def /*= 0*/ ) LITEHTML_RETURN_FUNC(0) -litehtml::uint_ptr litehtml::element::get_font( font_metrics* fm /*= 0*/ ) LITEHTML_RETURN_FUNC(0) -int litehtml::element::get_font_size() const LITEHTML_RETURN_FUNC(0) -void litehtml::element::get_text( tstring& text ) LITEHTML_EMPTY_FUNC -void litehtml::element::parse_attributes() LITEHTML_EMPTY_FUNC -int litehtml::element::select( const css_selector& selector, bool apply_pseudo) LITEHTML_RETURN_FUNC(select_no_match) -int litehtml::element::select( const css_element_selector& selector, bool apply_pseudo /*= true*/ ) LITEHTML_RETURN_FUNC(select_no_match) -litehtml::element::ptr litehtml::element::find_ancestor(const css_selector& selector, bool apply_pseudo, bool* is_pseudo) LITEHTML_RETURN_FUNC(0) -bool litehtml::element::is_first_child_inline(const element::ptr& el) const LITEHTML_RETURN_FUNC(false) -bool litehtml::element::is_last_child_inline(const element::ptr& el) LITEHTML_RETURN_FUNC(false) -bool litehtml::element::have_inline_child() const LITEHTML_RETURN_FUNC(false) +} // namespace litehtml +\ No newline at end of file diff --git a/src/plugins/litehtml_viewer/litehtml/element.h b/src/plugins/litehtml_viewer/litehtml/element.h @@ -2,341 +2,166 @@ #define LH_ELEMENT_H #include <memory> +#include <tuple> +#include <list> #include "stylesheet.h" #include "css_offsets.h" +#include "css_margins.h" +#include "css_properties.h" namespace litehtml { - class box; + class line_box; + class dumper; + class render_item; class element : public std::enable_shared_from_this<element> { - friend class block_box; friend class line_box; friend class html_tag; friend class el_table; friend class document; public: - typedef std::shared_ptr<litehtml::element> ptr; - typedef std::shared_ptr<const litehtml::element> const_ptr; - typedef std::weak_ptr<litehtml::element> weak_ptr; + typedef std::shared_ptr<element> ptr; + typedef std::shared_ptr<const element> const_ptr; + typedef std::weak_ptr<element> weak_ptr; protected: - std::weak_ptr<element> m_parent; - std::weak_ptr<litehtml::document> m_doc; - litehtml::box* m_box; - elements_vector m_children; - position m_pos; - margins m_margins; - margins m_padding; - margins m_borders; - bool m_skip; - + std::weak_ptr<element> m_parent; + std::weak_ptr<document> m_doc; + elements_vector m_children; + css_properties m_css; + std::list<std::weak_ptr<render_item>> m_renders; + used_selector::vector m_used_styles; + virtual void select_all(const css_selector& selector, elements_vector& res); + element::ptr _add_before_after(int type, const style& style); public: - element(const std::shared_ptr<litehtml::document>& doc); - virtual ~element(); - - // returns refer to m_pos member; - position& get_position(); - - int left() const; - int right() const; - int top() const; - int bottom() const; - int height() const; - int width() const; - - int content_margins_top() const; - int content_margins_bottom() const; - int content_margins_left() const; - int content_margins_right() const; - int content_margins_width() const; - int content_margins_height() const; + explicit element(const std::shared_ptr<document>& doc); + virtual ~element() = default; - int margin_top() const; - int margin_bottom() const; - int margin_left() const; - int margin_right() const; - margins get_margins() const; - - int padding_top() const; - int padding_bottom() const; - int padding_left() const; - int padding_right() const; - margins get_paddings() const; - - int border_top() const; - int border_bottom() const; - int border_left() const; - int border_right() const; - margins get_borders() const; + const css_properties& css() const; + css_properties& css_w(); bool in_normal_flow() const; - litehtml::web_color get_color(const tchar_t* prop_name, bool inherited, const litehtml::web_color& def_color = litehtml::web_color()); bool is_inline_box() const; + bool is_block_box() const; position get_placement() const; - bool collapse_top_margin() const; - bool collapse_bottom_margin() const; bool is_positioned() const; + bool is_float() const; - bool skip(); - void skip(bool val); bool have_parent() const; + bool is_root() const; element::ptr parent() const; - void parent(element::ptr par); - bool is_visible() const; - int calc_width(int defVal) const; - int get_inline_shift_left(); - int get_inline_shift_right(); - void apply_relative_shift(int parent_width); + void parent(const element::ptr& par); + // returns true for elements inside a table (but outside cells) that don't participate in table rendering + bool is_table_skip() const; std::shared_ptr<document> get_document() const; - virtual elements_vector select_all(const tstring& selector); + virtual elements_vector select_all(const string& selector); virtual elements_vector select_all(const css_selector& selector); - virtual element::ptr select_one(const tstring& selector); + virtual element::ptr select_one(const string& selector); virtual element::ptr select_one(const css_selector& selector); - virtual int render(int x, int y, int max_width, bool second_pass = false); - virtual int render_inline(const ptr &container, int max_width); - virtual int place_element(const ptr &el, int max_width); - virtual void calc_outlines( int parent_width ); - virtual void calc_auto_margins(int parent_width); - virtual void apply_vertical_align(); - virtual bool fetch_positioned(); - virtual void render_positioned(render_type rt = render_all); - virtual bool appendChild(const ptr &el); virtual bool removeChild(const ptr &el); virtual void clearRecursive(); - virtual const tchar_t* get_tagName() const; - virtual void set_tagName(const tchar_t* tag); - virtual void set_data(const tchar_t* data); - virtual element_float get_float() const; - virtual vertical_align get_vertical_align() const; - virtual element_clear get_clear() const; + virtual string_id id() const; + virtual string_id tag() const; + virtual const char* get_tagName() const; + virtual void set_tagName(const char* tag); + virtual void set_data(const char* data); virtual size_t get_children_count() const; virtual element::ptr get_child(int idx) const; - virtual overflow get_overflow() const; - virtual css_length get_css_left() const; - virtual css_length get_css_right() const; - virtual css_length get_css_top() const; - virtual css_length get_css_bottom() const; - virtual css_offsets get_css_offsets() const; - virtual css_length get_css_width() const; - virtual void set_css_width(css_length& w); - virtual css_length get_css_height() const; - - virtual void set_attr(const tchar_t* name, const tchar_t* val); - virtual const tchar_t* get_attr(const tchar_t* name, const tchar_t* def = 0) const; + virtual void set_attr(const char* name, const char* val); + virtual const char* get_attr(const char* name, const char* def = nullptr) const; virtual void apply_stylesheet(const litehtml::css& stylesheet); virtual void refresh_styles(); virtual bool is_white_space() const; + virtual bool is_space() const; + virtual bool is_comment() const; virtual bool is_body() const; virtual bool is_break() const; - virtual int get_base_line(); + virtual bool is_text() const; + virtual bool on_mouse_over(); virtual bool on_mouse_leave(); virtual bool on_lbutton_down(); virtual bool on_lbutton_up(); virtual void on_click(); - virtual bool find_styles_changes(position::vector& redraw_boxes, int x, int y); - virtual const tchar_t* get_cursor(); - virtual void init_font(); - virtual bool is_point_inside(int x, int y); - virtual bool set_pseudo_class(const tchar_t* pclass, bool add); - virtual bool set_class(const tchar_t* pclass, bool add); + virtual bool set_pseudo_class(string_id cls, bool add); + virtual bool set_class(const char* pclass, bool add); virtual bool is_replaced() const; - virtual int line_height() const; - virtual white_space get_white_space() const; - virtual style_display get_display() const; - virtual visibility get_visibility() const; - virtual element_position get_element_position(css_offsets* offsets = 0) const; - virtual void get_inline_boxes(position::vector& boxes); - virtual void parse_styles(bool is_reparse = false); - virtual void draw(uint_ptr hdc, int x, int y, const position* clip); - virtual void draw_background( uint_ptr hdc, int x, int y, const position* clip ); - virtual const tchar_t* get_style_property(const tchar_t* name, bool inherited, const tchar_t* def = 0); - virtual uint_ptr get_font(font_metrics* fm = 0); - virtual int get_font_size() const; - virtual void get_text(tstring& text); + virtual void compute_styles(bool recursive = true); + 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 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; + virtual float get_number_property(string_id name, bool inherited, float default_value, uint_ptr css_properties_member_offset) const; + virtual string_vector get_string_vector_property(string_id name, bool inherited, const string_vector& default_value, uint_ptr css_properties_member_offset) const; + virtual int_vector get_int_vector_property (string_id name, bool inherited, const int_vector& default_value, uint_ptr css_properties_member_offset) const; + virtual length_vector get_length_vector_property(string_id name, bool inherited, const length_vector& default_value, uint_ptr css_properties_member_offset) const; + virtual size_vector get_size_vector_property (string_id name, bool inherited, const size_vector& default_value, uint_ptr css_properties_member_offset) const; + virtual string get_custom_property(string_id name, const string& default_value) const; + + virtual void get_text(string& text); virtual void parse_attributes(); + virtual int select(const string& selector); virtual int select(const css_selector& selector, bool apply_pseudo = true); virtual int select(const css_element_selector& selector, bool apply_pseudo = true); - virtual element::ptr find_ancestor(const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = 0); + virtual element::ptr find_ancestor(const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = nullptr); virtual bool is_ancestor(const ptr &el) const; - virtual element::ptr find_adjacent_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = 0); - virtual element::ptr find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = 0); - virtual bool is_first_child_inline(const element::ptr& el) const; - virtual bool is_last_child_inline(const element::ptr& el); - virtual bool have_inline_child() const; + virtual element::ptr find_adjacent_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = nullptr); + virtual element::ptr find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = nullptr); virtual void get_content_size(size& sz, int max_width); - virtual void init(); virtual bool is_floats_holder() const; - virtual int get_floats_height(element_float el_float = float_none) const; - virtual int get_left_floats_height() const; - virtual int get_right_floats_height() const; - virtual int get_line_left(int y); - virtual int get_line_right(int y, int def_right); - virtual void get_line_left_right(int y, int def_right, int& ln_left, int& ln_right); - virtual void add_float(const ptr &el, int x, int y); virtual void update_floats(int dy, const ptr &parent); - virtual void add_positioned(const ptr &el); - virtual int find_next_line_top(int top, int width, int def_right); - virtual int get_zindex() const; - virtual void draw_stacking_context(uint_ptr hdc, int x, int y, const position* clip, bool with_positioned); - virtual void draw_children( uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex ); virtual bool is_nth_child(const element::ptr& el, int num, int off, bool of_type) const; virtual bool is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const; virtual bool is_only_child(const element::ptr& el, bool of_type) const; - virtual bool get_predefined_height(int& p_height) const; - virtual void calc_document_size(litehtml::size& sz, int x = 0, int y = 0); - virtual void get_redraw_box(litehtml::position& pos, int x = 0, int y = 0); - virtual void add_style(const litehtml::style& st); - virtual element::ptr get_element_by_point(int x, int y, int client_x, int client_y); - virtual element::ptr get_child_by_point(int x, int y, int client_x, int client_y, draw_flag flag, int zindex); + virtual void add_style(const style& style); virtual const background* get_background(bool own_only = false); + + virtual string dump_get_name(); + virtual std::vector<std::tuple<string, string>> dump_get_attrs(); + void dump(litehtml::dumper& cout); + + std::tuple<element::ptr, element::ptr, element::ptr> split_inlines(); + virtual std::shared_ptr<render_item> create_render_item(const std::shared_ptr<render_item>& parent_ri); + bool requires_styles_update(); + void add_render(const std::shared_ptr<render_item>& ri); + bool find_styles_changes( position::vector& redraw_boxes); + element::ptr add_pseudo_before(const style& style) + { + return _add_before_after(0, style); + } + element::ptr add_pseudo_after(const style& style) + { + return _add_before_after(1, style); + } }; ////////////////////////////////////////////////////////////////////////// // INLINE FUNCTIONS // ////////////////////////////////////////////////////////////////////////// - inline int litehtml::element::right() const - { - return left() + width(); - } - - inline int litehtml::element::left() const - { - return m_pos.left() - margin_left() - m_padding.left - m_borders.left; - } - - inline int litehtml::element::top() const - { - return m_pos.top() - margin_top() - m_padding.top - m_borders.top; - } - - inline int litehtml::element::bottom() const - { - return top() + height(); - } - - inline int litehtml::element::height() const - { - return m_pos.height + margin_top() + margin_bottom() + m_padding.height() + m_borders.height(); - } - - inline int litehtml::element::width() const - { - return m_pos.width + margin_left() + margin_right() + m_padding.width() + m_borders.width(); - } - - inline int litehtml::element::content_margins_top() const - { - return margin_top() + m_padding.top + m_borders.top; - } - - inline int litehtml::element::content_margins_bottom() const - { - return margin_bottom() + m_padding.bottom + m_borders.bottom; - } - - inline int litehtml::element::content_margins_left() const - { - return margin_left() + m_padding.left + m_borders.left; - } - - inline int litehtml::element::content_margins_right() const - { - return margin_right() + m_padding.right + m_borders.right; - } - - inline int litehtml::element::content_margins_width() const - { - return content_margins_left() + content_margins_right(); - } - - inline int litehtml::element::content_margins_height() const - { - return content_margins_top() + content_margins_bottom(); - } - - inline litehtml::margins litehtml::element::get_paddings() const - { - return m_padding; - } - - inline litehtml::margins litehtml::element::get_borders() const - { - return m_borders; - } - - inline int litehtml::element::padding_top() const - { - return m_padding.top; - } - - inline int litehtml::element::padding_bottom() const - { - return m_padding.bottom; - } - - inline int litehtml::element::padding_left() const - { - return m_padding.left; - } - - inline int litehtml::element::padding_right() const - { - return m_padding.right; - } - inline bool litehtml::element::in_normal_flow() const { - if(get_element_position() != element_position_absolute && get_display() != display_none) + if(css().get_position() != element_position_absolute && css().get_display() != display_none) { return true; } return false; } - inline int litehtml::element::border_top() const - { - return m_borders.top; - } - - inline int litehtml::element::border_bottom() const - { - return m_borders.bottom; - } - - inline int litehtml::element::border_left() const - { - return m_borders.left; - } - - inline int litehtml::element::border_right() const - { - return m_borders.right; - } - - inline bool litehtml::element::skip() + inline bool litehtml::element::is_root() const { - return m_skip; - } - - inline void litehtml::element::skip(bool val) - { - m_skip = val; - } - - inline bool litehtml::element::have_parent() const - { - return !m_parent.expired(); + return m_parent.expired(); } inline element::ptr litehtml::element::parent() const @@ -344,60 +169,47 @@ namespace litehtml return m_parent.lock(); } - inline void litehtml::element::parent(element::ptr par) + inline void litehtml::element::parent(const element::ptr& par) { m_parent = par; } - inline int litehtml::element::margin_top() const - { - return m_margins.top; - } - - inline int litehtml::element::margin_bottom() const - { - return m_margins.bottom; - } - - inline int litehtml::element::margin_left() const - { - return m_margins.left; - } - - inline int litehtml::element::margin_right() const + inline bool litehtml::element::is_positioned() const { - return m_margins.right; + return (css().get_position() > element_position_static); } - inline litehtml::margins litehtml::element::get_margins() const + inline bool litehtml::element::is_float() const { - margins ret; - ret.left = margin_left(); - ret.right = margin_right(); - ret.top = margin_top(); - ret.bottom = margin_bottom(); - - return ret; + return (css().get_float() != float_none); } - inline bool litehtml::element::is_positioned() const + inline std::shared_ptr<document> element::get_document() const { - return (get_element_position() > element_position_static); + return m_doc.lock(); } - inline bool litehtml::element::is_visible() const + inline const css_properties& element::css() const { - return !(m_skip || get_display() == display_none || get_visibility() != visibility_visible); + return m_css; } - inline position& litehtml::element::get_position() + inline css_properties& element::css_w() { - return m_pos; + return m_css; } - inline std::shared_ptr<document> element::get_document() const + inline bool element::is_block_box() const { - return m_doc.lock(); + if (css().get_display() == display_block || + css().get_display() == display_flex || + css().get_display() == display_table || + css().get_display() == display_list_item || + css().get_display() == display_flex) + { + return true; + } + return false; } } diff --git a/src/plugins/litehtml_viewer/litehtml/html.cpp b/src/plugins/litehtml_viewer/litehtml/html.cpp @@ -1,33 +1,38 @@ #include "html.h" #include "types.h" -#include "html_tag.h" +#include "utf8_strings.h" -void litehtml::trim(tstring &s) +void litehtml::trim(string &s) { - tstring::size_type pos = s.find_first_not_of(_t(" \n\r\t")); - if(pos != tstring::npos) + string::size_type pos = s.find_first_not_of(" \n\r\t"); + if(pos != string::npos) { s.erase(s.begin(), s.begin() + pos); } - pos = s.find_last_not_of(_t(" \n\r\t")); - if(pos != tstring::npos) + else + { + s = ""; + return; + } + pos = s.find_last_not_of(" \n\r\t"); + if(pos != string::npos) { s.erase(s.begin() + pos + 1, s.end()); } } -void litehtml::lcase(tstring &s) +void litehtml::lcase(string &s) { - for(tstring::iterator i = s.begin(); i != s.end(); i++) + for(char & i : s) { - (*i) = t_tolower(*i); + i = t_tolower(i); } } -litehtml::tstring::size_type litehtml::find_close_bracket(const tstring &s, tstring::size_type off, tchar_t open_b, tchar_t close_b) +litehtml::string::size_type litehtml::find_close_bracket(const string &s, string::size_type off, char open_b, char close_b) { int cnt = 0; - for(tstring::size_type i = off; i < s.length(); i++) + for(string::size_type i = off; i < s.length(); i++) { if(s[i] == open_b) { @@ -41,10 +46,23 @@ litehtml::tstring::size_type litehtml::find_close_bracket(const tstring &s, tstr } } } - return tstring::npos; + return string::npos; +} + +litehtml::string litehtml::index_value(int index, const string& strings, char delim) +{ + std::vector<string> vals; + string delims; + delims.push_back(delim); + split_string(strings, vals, delims); + if(index >= 0 && index < vals.size()) + { + return vals[index]; + } + return std::to_string(index); } -int litehtml::value_index( const tstring& val, const tstring& strings, int defValue, tchar_t delim ) +int litehtml::value_index( const string& val, const string& strings, int defValue, char delim ) { if(val.empty() || strings.empty() || !delim) { @@ -52,12 +70,12 @@ int litehtml::value_index( const tstring& val, const tstring& strings, int defVa } int idx = 0; - tstring::size_type delim_start = 0; - tstring::size_type delim_end = strings.find(delim, delim_start); - tstring::size_type item_len = 0; + string::size_type delim_start = 0; + string::size_type delim_end = strings.find(delim, delim_start); + string::size_type item_len; while(true) { - if(delim_end == tstring::npos) + if(delim_end == string::npos) { item_len = strings.length() - delim_start; } else @@ -73,7 +91,7 @@ int litehtml::value_index( const tstring& val, const tstring& strings, int defVa } idx++; delim_start = delim_end; - if(delim_start == tstring::npos) break; + if(delim_start == string::npos) break; delim_start++; if(delim_start == strings.length()) break; delim_end = strings.find(delim, delim_start); @@ -81,7 +99,7 @@ int litehtml::value_index( const tstring& val, const tstring& strings, int defVa return defValue; } -bool litehtml::value_in_list( const tstring& val, const tstring& strings, tchar_t delim ) +bool litehtml::value_in_list( const string& val, const string& strings, char delim ) { int idx = value_index(val, strings, -1, delim); if(idx >= 0) @@ -91,45 +109,45 @@ bool litehtml::value_in_list( const tstring& val, const tstring& strings, tchar_ return false; } -void litehtml::split_string(const tstring& str, string_vector& tokens, const tstring& delims, const tstring& delims_preserve, const tstring& quote) +void litehtml::split_string(const string& str, string_vector& tokens, const string& delims, const string& delims_preserve, const string& quote) { if(str.empty() || (delims.empty() && delims_preserve.empty())) { return; } - tstring all_delims = delims + delims_preserve + quote; + string all_delims = delims + delims_preserve + quote; - tstring::size_type token_start = 0; - tstring::size_type token_end = str.find_first_of(all_delims, token_start); - tstring::size_type token_len = 0; - tstring token; + string::size_type token_start = 0; + string::size_type token_end = str.find_first_of(all_delims, token_start); + string::size_type token_len; + string token; while(true) { - while( token_end != tstring::npos && quote.find_first_of(str[token_end]) != tstring::npos ) + while( token_end != string::npos && quote.find_first_of(str[token_end]) != string::npos ) { - if(str[token_end] == _t('(')) + if(str[token_end] == '(') { - token_end = find_close_bracket(str, token_end, _t('('), _t(')')); - } else if(str[token_end] == _t('[')) + token_end = find_close_bracket(str, token_end, '(', ')'); + } else if(str[token_end] == '[') { - token_end = find_close_bracket(str, token_end, _t('['), _t(']')); - } else if(str[token_end] == _t('{')) + token_end = find_close_bracket(str, token_end, '[', ']'); + } else if(str[token_end] == '{') { - token_end = find_close_bracket(str, token_end, _t('{'), _t('}')); + token_end = find_close_bracket(str, token_end, '{', '}'); } else { token_end = str.find_first_of(str[token_end], token_end + 1); } - if(token_end != tstring::npos) + if(token_end != string::npos) { token_end = str.find_first_of(all_delims, token_end + 1); } } - if(token_end == tstring::npos) + if(token_end == string::npos) { - token_len = tstring::npos; + token_len = string::npos; } else { token_len = token_end - token_start; @@ -140,30 +158,122 @@ void litehtml::split_string(const tstring& str, string_vector& tokens, const tst { tokens.push_back( token ); } - if(token_end != tstring::npos && !delims_preserve.empty() && delims_preserve.find_first_of(str[token_end]) != tstring::npos) + if(token_end != string::npos && !delims_preserve.empty() && delims_preserve.find_first_of(str[token_end]) != string::npos) { tokens.push_back( str.substr(token_end, 1) ); } token_start = token_end; - if(token_start == tstring::npos) break; + if(token_start == string::npos) break; token_start++; if(token_start == str.length()) break; token_end = str.find_first_of(all_delims, token_start); } } -void litehtml::join_string(tstring& str, const string_vector& tokens, const tstring& delims) +void litehtml::join_string(string& str, const string_vector& tokens, const string& delims) { - tstringstream ss; - for(size_t i=0; i<tokens.size(); ++i) + str = ""; + for (size_t i = 0; i < tokens.size(); i++) { - if(i != 0) + if (i != 0) { - ss << delims; + str += delims; } - ss << tokens[i]; + str += tokens[i]; + } +} + +int litehtml::t_strcasecmp(const char *s1, const char *s2) +{ + int i, d, c; + + for (i = 0;; i++) + { + c = t_tolower((unsigned char)s1[i]); + d = c - t_tolower((unsigned char)s2[i]); + if (d < 0) + return -1; + else if (d > 0) + return 1; + else if (c == 0) + return 0; + } +} + +int litehtml::t_strncasecmp(const char *s1, const char *s2, size_t n) +{ + int i, d, c; + + for (i = 0; i < n; i++) + { + c = t_tolower((unsigned char)s1[i]); + d = c - t_tolower((unsigned char)s2[i]); + if (d < 0) + return -1; + else if (d > 0) + return 1; + else if (c == 0) + return 0; } - str = ss.str(); + return 0; +} + +litehtml::string litehtml::get_escaped_string(const string& in_str) +{ + string ret; + for (auto ch : in_str) + { + switch (ch) + { + case '\'': + ret += "\\'"; + break; + + case '\"': + ret += "\\\""; + break; + + case '\?': + ret += "\\?"; + break; + + case '\\': + ret += "\\\\"; + break; + + case '\a': + ret += "\\a"; + break; + + case '\b': + ret += "\\b"; + break; + + case '\f': + ret += "\\f"; + break; + + case '\n': + ret += "\\n"; + break; + + case '\r': + ret += "\\r"; + break; + + case '\t': + ret += "\\t"; + break; + + case '\v': + ret += "\\v"; + break; + + default: + ret += ch; + } + } + return ret; } diff --git a/src/plugins/litehtml_viewer/litehtml/html.h b/src/plugins/litehtml_viewer/litehtml/html.h @@ -8,73 +8,50 @@ #include <map> #include <cstring> #include <algorithm> -#include <sstream> -#include <cstdint> +#include <functional> #include "os_types.h" +#include "string_id.h" #include "types.h" +#include "utf8_strings.h" #include "background.h" #include "borders.h" -#include "html_tag.h" #include "web_color.h" #include "media_query.h" +#include "html_tag.h" +#include "document_container.h" +#include "document.h" namespace litehtml { - struct list_marker - { - tstring image; - const tchar_t* baseurl; - list_style_type marker_type; - web_color color; - position pos; - int index; - uint_ptr font; - }; + void trim(string &s); + 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 = ';'); + bool value_in_list(const string& val, const string& strings, char delim = ';'); + string::size_type find_close_bracket(const string &s, string::size_type off, char open_b = '(', char close_b = ')'); + void split_string(const string& str, string_vector& tokens, const string& delims, const string& delims_preserve = "", const string& quote = "\""); + void join_string(string& str, const string_vector& tokens, const string& delims); + double t_strtod(const char* string, char** endPtr = nullptr); + string get_escaped_string(const string& in_str); - // call back interface to draw text, images and other elements - class document_container + int t_strcasecmp(const char *s1, const char *s2); + int t_strncasecmp(const char *s1, const char *s2, size_t n); + + inline int t_isdigit(int c) { - public: - virtual litehtml::uint_ptr create_font(const litehtml::tchar_t* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm) = 0; - virtual void delete_font(litehtml::uint_ptr hFont) = 0; - virtual int text_width(const litehtml::tchar_t* text, litehtml::uint_ptr hFont) = 0; - virtual void draw_text(litehtml::uint_ptr hdc, const litehtml::tchar_t* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos) = 0; - virtual int pt_to_px(int pt) = 0; - virtual int get_default_font_size() const = 0; - virtual const litehtml::tchar_t* get_default_font_name() const = 0; - virtual void draw_list_marker(litehtml::uint_ptr hdc, const litehtml::list_marker& marker) = 0; - virtual void load_image(const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, bool redraw_on_ready) = 0; - virtual void get_image_size(const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, litehtml::size& sz) = 0; - virtual void draw_background(litehtml::uint_ptr hdc, const litehtml::background_paint& bg) = 0; - virtual void draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root) = 0; - - virtual void set_caption(const litehtml::tchar_t* caption) = 0; - virtual void set_base_url(const litehtml::tchar_t* base_url) = 0; - virtual void link(const std::shared_ptr<litehtml::document>& doc, const litehtml::element::ptr& el) = 0; - virtual void on_anchor_click(const litehtml::tchar_t* url, const litehtml::element::ptr& el) = 0; - virtual void set_cursor(const litehtml::tchar_t* cursor) = 0; - virtual void transform_text(litehtml::tstring& text, litehtml::text_transform tt) = 0; - virtual void import_css(litehtml::tstring& text, const litehtml::tstring& url, litehtml::tstring& baseurl) = 0; - virtual void set_clip(const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius, bool valid_x, bool valid_y) = 0; - virtual void del_clip() = 0; - virtual void get_client_rect(litehtml::position& client) const = 0; - virtual std::shared_ptr<litehtml::element> create_element(const litehtml::tchar_t *tag_name, - const litehtml::string_map &attributes, - const std::shared_ptr<litehtml::document> &doc) = 0; - - virtual void get_media_features(litehtml::media_features& media) const = 0; - virtual void get_language(litehtml::tstring& language, litehtml::tstring & culture) const = 0; - virtual litehtml::tstring resolve_color(const litehtml::tstring& color) const { return litehtml::tstring(); } - }; + return (c >= '0' && c <= '9'); + } - void trim(tstring &s); - void lcase(tstring &s); - int value_index(const tstring& val, const tstring& strings, int defValue = -1, tchar_t delim = _t(';')); - bool value_in_list(const tstring& val, const tstring& strings, tchar_t delim = _t(';')); - tstring::size_type find_close_bracket(const tstring &s, tstring::size_type off, tchar_t open_b = _t('('), tchar_t close_b = _t(')')); - void split_string(const tstring& str, string_vector& tokens, const tstring& delims, const tstring& delims_preserve = _t(""), const tstring& quote = _t("\"")); - void join_string(tstring& str, const string_vector& tokens, const tstring& delims); + inline int t_isalpha(int c) + { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + } + inline int t_tolower(int c) + { + return (c >= 'A' && c <= 'Z' ? c + 'a' - 'A' : c); + } + inline int round_f(float val) { int int_val = (int) val; @@ -94,6 +71,16 @@ namespace litehtml } return int_val; } + + inline float t_strtof(const string& str, char** endPtr = nullptr) + { + return (float)t_strtod(str.c_str(), endPtr); + } + + inline int baseline_align(int line_height, int line_base_line, int height, int baseline) + { + return (line_height - line_base_line) - (height - baseline); + } } #endif // LH_HTML_H diff --git a/src/plugins/litehtml_viewer/litehtml/html_tag.cpp b/src/plugins/litehtml_viewer/litehtml/html_tag.cpp @@ -8,35 +8,25 @@ #include <locale> #include "el_before_after.h" #include "num_cvt.h" +#include "line_box.h" +#include <stack> +#include "render_item.h" -litehtml::html_tag::html_tag(const std::shared_ptr<litehtml::document>& doc) : litehtml::element(doc) +litehtml::html_tag::html_tag(const std::shared_ptr<document>& doc) : element(doc) { - m_box_sizing = box_sizing_content_box; - m_z_index = 0; - m_overflow = overflow_visible; - m_box = 0; - m_text_align = text_align_left; - m_el_position = element_position_static; - m_display = display_inline; - m_vertical_align = va_baseline; - m_list_style_type = list_style_type_none; - m_list_style_position = list_style_position_outside; - m_float = float_none; - m_clear = clear_none; - m_font = 0; - m_font_size = 0; - m_white_space = white_space_normal; - m_lh_predefined = false; - m_line_height = 0; - m_visibility = visibility_visible; - m_border_spacing_x = 0; - m_border_spacing_y = 0; - m_border_collapse = border_collapse_separate; + m_tag = empty_id; + m_id = empty_id; } -litehtml::html_tag::~html_tag() +litehtml::html_tag::html_tag(const element::ptr& parent, const string& style) : element(parent->get_document()), + m_tag(empty_id), + m_id(empty_id) { - + litehtml::style st; + st.add(style); + add_style(st); + this->parent(parent); + compute_styles(); } bool litehtml::html_tag::appendChild(const element::ptr &el) @@ -71,34 +61,59 @@ void litehtml::html_tag::clearRecursive() m_children.clear(); } +litehtml::string_id litehtml::html_tag::id() const +{ + return m_id; +} + +litehtml::string_id litehtml::html_tag::tag() const +{ + return m_tag; +} + +const char* litehtml::html_tag::get_tagName() const +{ + return _s(m_tag).c_str(); +} -const litehtml::tchar_t* litehtml::html_tag::get_tagName() const +void litehtml::html_tag::set_tagName( const char* _tag ) { - return m_tag.c_str(); + string tag = _tag; + lcase(tag); + m_tag = _id(tag); } -void litehtml::html_tag::set_attr( const tchar_t* name, const tchar_t* val ) +void litehtml::html_tag::set_attr( const char* _name, const char* _val ) { - if(name && val) + if(_name && _val) { - tstring s_val = name; - for(size_t i = 0; i < s_val.length(); i++) + string name = _name; + lcase(name); + m_attrs[name] = _val; + + if( name == "class" ) { - s_val[i] = std::tolower(s_val[i], std::locale::classic()); + string val = _val; + // class names are matched case-insensitively in quirks mode + // we match them case-insensitively in all modes (same for id) + lcase(val); + m_str_classes.resize( 0 ); + split_string( val, m_str_classes, " " ); + m_classes.clear(); + for (auto& cls : m_str_classes) m_classes.push_back(_id(cls)); } - m_attrs[s_val] = val; - - if( t_strcasecmp( name, _t("class") ) == 0 ) + else if (name == "id") { - m_class_values.resize( 0 ); - split_string( val, m_class_values, _t(" ") ); + string val = _val; + lcase(val); + m_id = _id(val); } } } -const litehtml::tchar_t* litehtml::html_tag::get_attr( const tchar_t* name, const tchar_t* def ) const +const char* litehtml::html_tag::get_attr( const char* name, const char* def ) const { - string_map::const_iterator attr = m_attrs.find(name); + auto attr = m_attrs.find(name); if(attr != m_attrs.end()) { return attr->second.c_str(); @@ -106,9 +121,9 @@ const litehtml::tchar_t* litehtml::html_tag::get_attr( const tchar_t* name, cons return def; } -litehtml::elements_vector litehtml::html_tag::select_all( const tstring& selector ) +litehtml::elements_vector litehtml::html_tag::select_all( const string& selector ) { - css_selector sel(media_query_list::ptr(0)); + css_selector sel; sel.parse(selector); return select_all(sel); @@ -135,9 +150,9 @@ void litehtml::html_tag::select_all(const css_selector& selector, elements_vecto } -litehtml::element::ptr litehtml::html_tag::select_one( const tstring& selector ) +litehtml::element::ptr litehtml::html_tag::select_one( const string& selector ) { - css_selector sel(media_query_list::ptr(0)); + css_selector sel; sel.parse(selector); return select_one(sel); @@ -158,15 +173,28 @@ litehtml::element::ptr litehtml::html_tag::select_one( const css_selector& selec return res; } } - return 0; + return nullptr; } void litehtml::html_tag::apply_stylesheet( const litehtml::css& stylesheet ) { - remove_before_after(); - for(const auto& sel : stylesheet.selectors()) { + // optimization + { + const auto& r = sel->m_right; + if (r.m_tag != star_id && r.m_tag != m_tag) + continue; + + if (!r.m_attrs.empty()) + { + const auto& attr = r.m_attrs[0]; + if (attr.type == select_class && + std::find(m_classes.begin(), m_classes.end(), attr.name) == m_classes.end()) + continue; + } + } + int apply = select(*sel, false); if(apply != select_no_match) @@ -181,14 +209,14 @@ void litehtml::html_tag::apply_stylesheet( const litehtml::css& stylesheet ) { if(apply & select_match_with_after) { - element::ptr el = get_element_after(); + element::ptr el = get_element_after(*sel->m_style, true); if(el) { el->add_style(*sel->m_style); } } else if(apply & select_match_with_before) { - element::ptr el = get_element_before(); + element::ptr el = get_element_before(*sel->m_style, true); if(el) { el->add_style(*sel->m_style); @@ -202,14 +230,14 @@ void litehtml::html_tag::apply_stylesheet( const litehtml::css& stylesheet ) } } else if(apply & select_match_with_after) { - element::ptr el = get_element_after(); + element::ptr el = get_element_after(*sel->m_style, true); if(el) { el->add_style(*sel->m_style); } } else if(apply & select_match_with_before) { - element::ptr el = get_element_before(); + element::ptr el = get_element_before(*sel->m_style, true); if(el) { el->add_style(*sel->m_style); @@ -226,7 +254,7 @@ void litehtml::html_tag::apply_stylesheet( const litehtml::css& stylesheet ) for(auto& el : m_children) { - if(el->get_display() != display_inline_text) + if(el->css().get_display() != display_inline_text) { el->apply_stylesheet(stylesheet); } @@ -236,7 +264,7 @@ void litehtml::html_tag::apply_stylesheet( const litehtml::css& stylesheet ) void litehtml::html_tag::get_content_size( size& sz, int max_width ) { sz.height = 0; - if(m_display == display_block) + if(m_css.get_display() == display_block) { sz.width = max_width; } else @@ -245,351 +273,154 @@ void litehtml::html_tag::get_content_size( size& sz, int max_width ) } } -void litehtml::html_tag::draw( uint_ptr hdc, int x, int y, const position* clip ) +void litehtml::html_tag::draw(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) { - position pos = m_pos; + position pos = ri->pos(); pos.x += x; pos.y += y; - draw_background(hdc, x, y, clip); + draw_background(hdc, x, y, clip, ri); - if(m_display == display_list_item && m_list_style_type != list_style_type_none) + if(m_css.get_display() == display_list_item && m_css.get_list_style_type() != list_style_type_none) { - if(m_overflow > overflow_visible) + if(m_css.get_overflow() > overflow_visible) { position border_box = pos; - border_box += m_padding; - border_box += m_borders; + border_box += ri->get_paddings(); + border_box += ri->get_borders(); - border_radiuses bdr_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height); + border_radiuses bdr_radius = m_css.get_borders().radius.calc_percents(border_box.width, border_box.height); - bdr_radius -= m_borders; - bdr_radius -= m_padding; + bdr_radius -= ri->get_borders(); + bdr_radius -= ri->get_paddings(); - get_document()->container()->set_clip(pos, bdr_radius, true, true); + get_document()->container()->set_clip(pos, bdr_radius); } draw_list_marker(hdc, pos); - if(m_overflow > overflow_visible) + if(m_css.get_overflow() > overflow_visible) { get_document()->container()->del_clip(); } } } -litehtml::uint_ptr litehtml::html_tag::get_font(font_metrics* fm) +litehtml::string litehtml::html_tag::get_custom_property(string_id name, const string& default_value) const { - if(fm) - { - *fm = m_font_metrics; - } - return m_font; -} + const property_value& value = m_style.get_property(name); -const litehtml::tchar_t* litehtml::html_tag::get_style_property( const tchar_t* name, bool inherited, const tchar_t* def /*= 0*/ ) -{ - const tchar_t* ret = m_style.get_property(name); - element::ptr el_parent = parent(); - if (el_parent) + if (value.m_type == prop_type_string) { - if ( ( ret && !t_strcasecmp(ret, _t("inherit")) ) || (!ret && inherited) ) - { - ret = el_parent->get_style_property(name, inherited, def); - } + return value.m_string; } - - if(!ret) + else if (auto _parent = parent()) { - ret = def; + return _parent->get_custom_property(name, default_value); } - - return ret; + return default_value; } -void litehtml::html_tag::parse_styles(bool is_reparse) +template<class Type, litehtml::property_type property_value_type, Type litehtml::property_value::* property_value_member> +const Type& litehtml::html_tag::get_property_impl(string_id name, bool inherited, const Type& default_value, uint_ptr css_properties_member_offset) const { - const tchar_t* style = get_attr(_t("style")); - - if(style) - { - m_style.add(style, NULL); - } + const property_value& value = m_style.get_property(name); - init_font(); - document::ptr doc = get_document(); - - m_el_position = (element_position) value_index(get_style_property(_t("position"), false, _t("static")), element_position_strings, element_position_fixed); - m_text_align = (text_align) value_index(get_style_property(_t("text-align"), true, _t("left")), text_align_strings, text_align_left); - m_overflow = (overflow) value_index(get_style_property(_t("overflow"), false, _t("visible")), overflow_strings, overflow_visible); - m_white_space = (white_space) value_index(get_style_property(_t("white-space"), true, _t("normal")), white_space_strings, white_space_normal); - m_display = (style_display) value_index(get_style_property(_t("display"), false, _t("inline")), style_display_strings, display_inline); - m_visibility = (visibility) value_index(get_style_property(_t("visibility"), true, _t("visible")), visibility_strings, visibility_visible); - m_box_sizing = (box_sizing) value_index(get_style_property(_t("box-sizing"), false, _t("content-box")), box_sizing_strings, box_sizing_content_box); - - if(m_el_position != element_position_static) - { - const tchar_t* val = get_style_property(_t("z-index"), false, 0); - if(val) - { - m_z_index = t_atoi(val); - } - } - - const tchar_t* va = get_style_property(_t("vertical-align"), true, _t("baseline")); - m_vertical_align = (vertical_align) value_index(va, vertical_align_strings, va_baseline); - - const tchar_t* fl = get_style_property(_t("float"), false, _t("none")); - m_float = (element_float) value_index(fl, element_float_strings, float_none); - - m_clear = (element_clear) value_index(get_style_property(_t("clear"), false, _t("none")), element_clear_strings, clear_none); - - if (m_float != float_none) - { - // reset display in to block for floating elements - if (m_display != display_none) - { - m_display = display_block; - } - } - else if (m_display == display_table || - m_display == display_inline_table || - m_display == display_table_caption || - m_display == display_table_cell || - m_display == display_table_column || - m_display == display_table_column_group || - m_display == display_table_footer_group || - m_display == display_table_header_group || - m_display == display_table_row || - m_display == display_table_row_group) + if (value.m_type == property_value_type) { - doc->add_tabular(shared_from_this()); + return value.*property_value_member; } - // fix inline boxes with absolute/fixed positions - else if (m_display != display_none && is_inline_box()) + else if (inherited || value.m_type == prop_type_inherit) { - if (m_el_position == element_position_absolute || m_el_position == element_position_fixed) + if (auto _parent = parent()) { - m_display = display_block; + return *(Type*)((byte*)&_parent->css() + css_properties_member_offset); } + return default_value; } + // value must be invalid here + //assert(value.m_type == prop_type_invalid); + return default_value; +} - m_css_text_indent.fromString( get_style_property(_t("text-indent"), true, _t("0")), _t("0")); - - m_css_width.fromString( get_style_property(_t("width"), false, _t("auto")), _t("auto")); - m_css_height.fromString( get_style_property(_t("height"), false, _t("auto")), _t("auto")); - - doc->cvt_units(m_css_width, m_font_size); - doc->cvt_units(m_css_height, m_font_size); - - m_css_min_width.fromString( get_style_property(_t("min-width"), false, _t("0"))); - m_css_min_height.fromString( get_style_property(_t("min-height"), false, _t("0"))); - - m_css_max_width.fromString( get_style_property(_t("max-width"), false, _t("none")), _t("none")); - m_css_max_height.fromString( get_style_property(_t("max-height"), false, _t("none")), _t("none")); - - doc->cvt_units(m_css_min_width, m_font_size); - doc->cvt_units(m_css_min_height, m_font_size); - - m_css_offsets.left.fromString( get_style_property(_t("left"), false, _t("auto")), _t("auto")); - m_css_offsets.right.fromString( get_style_property(_t("right"), false, _t("auto")), _t("auto")); - m_css_offsets.top.fromString( get_style_property(_t("top"), false, _t("auto")), _t("auto")); - m_css_offsets.bottom.fromString( get_style_property(_t("bottom"), false, _t("auto")), _t("auto")); - - doc->cvt_units(m_css_offsets.left, m_font_size); - doc->cvt_units(m_css_offsets.right, m_font_size); - doc->cvt_units(m_css_offsets.top, m_font_size); - doc->cvt_units(m_css_offsets.bottom, m_font_size); - - m_css_margins.left.fromString( get_style_property(_t("margin-left"), false, _t("0")), _t("auto")); - m_css_margins.right.fromString( get_style_property(_t("margin-right"), false, _t("0")), _t("auto")); - m_css_margins.top.fromString( get_style_property(_t("margin-top"), false, _t("0")), _t("auto")); - m_css_margins.bottom.fromString( get_style_property(_t("margin-bottom"), false, _t("0")), _t("auto")); - - m_css_padding.left.fromString( get_style_property(_t("padding-left"), false, _t("0")), _t("")); - m_css_padding.right.fromString( get_style_property(_t("padding-right"), false, _t("0")), _t("")); - m_css_padding.top.fromString( get_style_property(_t("padding-top"), false, _t("0")), _t("")); - m_css_padding.bottom.fromString( get_style_property(_t("padding-bottom"), false, _t("0")), _t("")); - - m_css_borders.left.width.fromString( get_style_property(_t("border-left-width"), false, _t("medium")), border_width_strings); - m_css_borders.right.width.fromString( get_style_property(_t("border-right-width"), false, _t("medium")), border_width_strings); - m_css_borders.top.width.fromString( get_style_property(_t("border-top-width"), false, _t("medium")), border_width_strings); - m_css_borders.bottom.width.fromString( get_style_property(_t("border-bottom-width"), false, _t("medium")), border_width_strings); - - m_css_borders.left.color = web_color::from_string(get_style_property(_t("border-left-color"), false, _t("")), doc->container()); - m_css_borders.left.style = (border_style) value_index(get_style_property(_t("border-left-style"), false, _t("none")), border_style_strings, border_style_none); - - m_css_borders.right.color = web_color::from_string(get_style_property(_t("border-right-color"), false, _t("")), doc->container()); - m_css_borders.right.style = (border_style) value_index(get_style_property(_t("border-right-style"), false, _t("none")), border_style_strings, border_style_none); - - m_css_borders.top.color = web_color::from_string(get_style_property(_t("border-top-color"), false, _t("")), doc->container()); - m_css_borders.top.style = (border_style) value_index(get_style_property(_t("border-top-style"), false, _t("none")), border_style_strings, border_style_none); - - m_css_borders.bottom.color = web_color::from_string(get_style_property(_t("border-bottom-color"), false, _t("")), doc->container()); - m_css_borders.bottom.style = (border_style) value_index(get_style_property(_t("border-bottom-style"), false, _t("none")), border_style_strings, border_style_none); - - m_css_borders.radius.top_left_x.fromString(get_style_property(_t("border-top-left-radius-x"), false, _t("0"))); - m_css_borders.radius.top_left_y.fromString(get_style_property(_t("border-top-left-radius-y"), false, _t("0"))); - - m_css_borders.radius.top_right_x.fromString(get_style_property(_t("border-top-right-radius-x"), false, _t("0"))); - m_css_borders.radius.top_right_y.fromString(get_style_property(_t("border-top-right-radius-y"), false, _t("0"))); - - m_css_borders.radius.bottom_right_x.fromString(get_style_property(_t("border-bottom-right-radius-x"), false, _t("0"))); - m_css_borders.radius.bottom_right_y.fromString(get_style_property(_t("border-bottom-right-radius-y"), false, _t("0"))); - - m_css_borders.radius.bottom_left_x.fromString(get_style_property(_t("border-bottom-left-radius-x"), false, _t("0"))); - m_css_borders.radius.bottom_left_y.fromString(get_style_property(_t("border-bottom-left-radius-y"), false, _t("0"))); - - doc->cvt_units(m_css_borders.radius.bottom_left_x, m_font_size); - doc->cvt_units(m_css_borders.radius.bottom_left_y, m_font_size); - doc->cvt_units(m_css_borders.radius.bottom_right_x, m_font_size); - doc->cvt_units(m_css_borders.radius.bottom_right_y, m_font_size); - doc->cvt_units(m_css_borders.radius.top_left_x, m_font_size); - doc->cvt_units(m_css_borders.radius.top_left_y, m_font_size); - doc->cvt_units(m_css_borders.radius.top_right_x, m_font_size); - doc->cvt_units(m_css_borders.radius.top_right_y, m_font_size); - - doc->cvt_units(m_css_text_indent, m_font_size); - - m_margins.left = doc->cvt_units(m_css_margins.left, m_font_size); - m_margins.right = doc->cvt_units(m_css_margins.right, m_font_size); - m_margins.top = doc->cvt_units(m_css_margins.top, m_font_size); - m_margins.bottom = doc->cvt_units(m_css_margins.bottom, m_font_size); - - m_padding.left = doc->cvt_units(m_css_padding.left, m_font_size); - m_padding.right = doc->cvt_units(m_css_padding.right, m_font_size); - m_padding.top = doc->cvt_units(m_css_padding.top, m_font_size); - m_padding.bottom = doc->cvt_units(m_css_padding.bottom, m_font_size); - - m_borders.left = doc->cvt_units(m_css_borders.left.width, m_font_size); - m_borders.right = doc->cvt_units(m_css_borders.right.width, m_font_size); - m_borders.top = doc->cvt_units(m_css_borders.top.width, m_font_size); - m_borders.bottom = doc->cvt_units(m_css_borders.bottom.width, m_font_size); - - css_length line_height; - line_height.fromString(get_style_property(_t("line-height"), true, _t("normal")), _t("normal")); - if(line_height.is_predefined()) - { - m_line_height = m_font_metrics.height; - m_lh_predefined = true; - } else if(line_height.units() == css_units_none) - { - m_line_height = (int) (line_height.val() * m_font_size); - m_lh_predefined = false; - } else - { - m_line_height = doc->cvt_units(line_height, m_font_size, m_font_size); - m_lh_predefined = false; - } - - - if(m_display == display_list_item) - { - const tchar_t* list_type = get_style_property(_t("list-style-type"), true, _t("disc")); - m_list_style_type = (list_style_type) value_index(list_type, list_style_type_strings, list_style_type_disc); - - const tchar_t* list_pos = get_style_property(_t("list-style-position"), true, _t("outside")); - m_list_style_position = (list_style_position) value_index(list_pos, list_style_position_strings, list_style_position_outside); - - const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0); - if(list_image && list_image[0]) - { - tstring url; - css::parse_css_url(list_image, url); - - const tchar_t* list_image_baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0); - doc->container()->load_image(url.c_str(), list_image_baseurl, true); - } +int litehtml::html_tag::get_enum_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); +} - parse_background(); +litehtml::web_color litehtml::html_tag::get_color_property(string_id name, bool inherited, web_color default_value, uint_ptr css_properties_member_offset) const +{ + return get_property_impl<web_color, prop_type_color, &property_value::m_color>(name, inherited, default_value, css_properties_member_offset); +} - if(!is_reparse) - { - for(auto& el : m_children) - { - el->parse_styles(); - } - } +litehtml::string litehtml::html_tag::get_string_property(string_id name, bool inherited, const string& default_value, uint_ptr css_properties_member_offset) const +{ + return get_property_impl<string, prop_type_string, &property_value::m_string>(name, inherited, default_value, css_properties_member_offset); } -int litehtml::html_tag::render( int x, int y, int max_width, bool second_pass ) +float litehtml::html_tag::get_number_property(string_id name, bool inherited, float default_value, uint_ptr css_properties_member_offset) const { - if (m_display == display_table || m_display == display_inline_table) - { - return render_table(x, y, max_width, second_pass); - } + return get_property_impl<float, prop_type_number, &property_value::m_number>(name, inherited, default_value, css_properties_member_offset); +} - return render_box(x, y, max_width, second_pass); +litehtml::string_vector litehtml::html_tag::get_string_vector_property(string_id name, bool inherited, const string_vector& default_value, uint_ptr css_properties_member_offset) const +{ + return get_property_impl<string_vector, prop_type_string_vector, &property_value::m_string_vector>(name, inherited, default_value, css_properties_member_offset); } -bool litehtml::html_tag::is_white_space() const +litehtml::int_vector litehtml::html_tag::get_int_vector_property(string_id name, bool inherited, const int_vector& default_value, uint_ptr css_properties_member_offset) const { - return false; + return get_property_impl<int_vector, prop_type_enum_item_vector, &property_value::m_enum_item_vector>(name, inherited, default_value, css_properties_member_offset); } -int litehtml::html_tag::get_font_size() const +litehtml::length_vector litehtml::html_tag::get_length_vector_property(string_id name, bool inherited, const length_vector& default_value, uint_ptr css_properties_member_offset) const { - return m_font_size; + return get_property_impl<length_vector, prop_type_length_vector, &property_value::m_length_vector>(name, inherited, default_value, css_properties_member_offset); } -int litehtml::html_tag::get_base_line() +litehtml::size_vector litehtml::html_tag::get_size_vector_property(string_id name, bool inherited, const size_vector& default_value, uint_ptr css_properties_member_offset) const { - if(is_replaced()) - { - return 0; - } - int bl = 0; - if(!m_boxes.empty()) - { - bl = m_boxes.back()->baseline() + content_margins_bottom(); - } - return bl; + return get_property_impl<size_vector, prop_type_size_vector, &property_value::m_size_vector>(name, inherited, default_value, css_properties_member_offset); } -void litehtml::html_tag::init() +void litehtml::html_tag::compute_styles(bool recursive) { - if (m_display == display_table || m_display == display_inline_table) + const char* style = get_attr("style"); + document::ptr doc = get_document(); + + if (style) { - if (m_grid) - { - m_grid->clear(); - } - else - { - m_grid = std::unique_ptr<table_grid>(new table_grid()); - } + m_style.add(style, "", doc->container()); + } - go_inside_table table_selector; - table_rows_selector row_selector; - table_cells_selector cell_selector; + m_style.subst_vars(this); - elements_iterator row_iter(shared_from_this(), &table_selector, &row_selector); + m_css.compute(this, doc); - element::ptr row = row_iter.next(false); - while (row) + if (recursive) + { + for (const auto& el : m_children) { - m_grid->begin_row(row); - - elements_iterator cell_iter(row, &table_selector, &cell_selector); - element::ptr cell = cell_iter.next(); - while (cell) - { - m_grid->add_cell(cell); - - cell = cell_iter.next(false); - } - row = row_iter.next(false); + el->compute_styles(); } - - m_grid->finish(); } +} - for (auto& el : m_children) - { - el->init(); - } +bool litehtml::html_tag::is_white_space() const +{ + return false; +} + +int litehtml::html_tag::select(const string& selector) +{ + css_selector sel; + sel.parse(selector); + return select(sel, true); } int litehtml::html_tag::select(const css_selector& selector, bool apply_pseudo) @@ -680,107 +511,34 @@ int litehtml::html_tag::select(const css_selector& selector, bool apply_pseudo) int litehtml::html_tag::select(const css_element_selector& selector, bool apply_pseudo) { - if(!selector.m_tag.empty() && selector.m_tag != _t("*")) + if(selector.m_tag != star_id && selector.m_tag != m_tag) { - if(selector.m_tag != m_tag) - { - return select_no_match; - } + return select_no_match; } int res = select_match; - element::ptr el_parent = parent(); - for(css_attribute_selector::vector::const_iterator i = selector.m_attrs.begin(); i != selector.m_attrs.end(); i++) + for(const auto& attr : selector.m_attrs) { - const tchar_t* attr_value = get_attr(i->attribute.c_str()); - switch(i->condition) + switch(attr.type) { - case select_exists: - if(!attr_value) - { - return select_no_match; - } - break; - case select_equal: - if(!attr_value) - { - return select_no_match; - } else - { - if(i->attribute == _t("class")) - { - const string_vector & tokens1 = m_class_values; - const string_vector & tokens2 = i->class_val; - bool found = true; - for(string_vector::const_iterator str1 = tokens2.begin(); str1 != tokens2.end() && found; str1++) - { - bool f = false; - for(string_vector::const_iterator str2 = tokens1.begin(); str2 != tokens1.end() && !f; str2++) - { - if( !t_strcasecmp(str1->c_str(), str2->c_str()) ) - { - f = true; - } - } - if(!f) - { - found = false; - } - } - if(!found) - { - return select_no_match; - } - } else - { - if( t_strcasecmp(i->val.c_str(), attr_value) ) - { - return select_no_match; - } - } - } - break; - case select_contain_str: - if(!attr_value) - { - return select_no_match; - } else if(!t_strstr(attr_value, i->val.c_str())) - { - return select_no_match; - } - break; - case select_start_str: - if(!attr_value) - { - return select_no_match; - } else if(t_strncmp(attr_value, i->val.c_str(), i->val.length())) + case select_class: + if (std::find(m_classes.begin(), m_classes.end(), attr.name) == m_classes.end()) { return select_no_match; } break; - case select_end_str: - if(!attr_value) + case select_id: + if (attr.name != m_id) { return select_no_match; - } else if(t_strncmp(attr_value, i->val.c_str(), i->val.length())) - { - const tchar_t* s = attr_value + t_strlen(attr_value) - i->val.length() - 1; - if(s < attr_value) - { - return select_no_match; - } - if(i->val != s) - { - return select_no_match; - } } break; case select_pseudo_element: - if(i->val == _t("after")) + if(attr.name == _after_) { res |= select_match_with_after; - } else if(i->val == _t("before")) + } else if(attr.name == _before_) { res |= select_match_with_before; } else @@ -791,3970 +549,1130 @@ int litehtml::html_tag::select(const css_element_selector& selector, bool apply_ case select_pseudo_class: if(apply_pseudo) { - if (!el_parent) return select_no_match; - - tstring selector_param; - tstring selector_name; - - tstring::size_type begin = i->val.find_first_of(_t('(')); - tstring::size_type end = (begin == tstring::npos) ? tstring::npos : find_close_bracket(i->val, begin); - if(begin != tstring::npos && end != tstring::npos) + if (select_pseudoclass(attr) == select_no_match) { - selector_param = i->val.substr(begin + 1, end - begin - 1); - } - if(begin != tstring::npos) - { - selector_name = i->val.substr(0, begin); - litehtml::trim(selector_name); - } else - { - selector_name = i->val; - } - - int selector = value_index(selector_name.c_str(), pseudo_class_strings); - - switch(selector) - { - case pseudo_class_only_child: - if (!el_parent->is_only_child(shared_from_this(), false)) - { - return select_no_match; - } - break; - case pseudo_class_only_of_type: - if (!el_parent->is_only_child(shared_from_this(), true)) - { - return select_no_match; - } - break; - case pseudo_class_first_child: - if (!el_parent->is_nth_child(shared_from_this(), 0, 1, false)) - { - return select_no_match; - } - break; - case pseudo_class_first_of_type: - if (!el_parent->is_nth_child(shared_from_this(), 0, 1, true)) - { - return select_no_match; - } - break; - case pseudo_class_last_child: - if (!el_parent->is_nth_last_child(shared_from_this(), 0, 1, false)) - { - return select_no_match; - } - break; - case pseudo_class_last_of_type: - if (!el_parent->is_nth_last_child(shared_from_this(), 0, 1, true)) - { - return select_no_match; - } - break; - case pseudo_class_nth_child: - case pseudo_class_nth_of_type: - case pseudo_class_nth_last_child: - case pseudo_class_nth_last_of_type: - { - if(selector_param.empty()) return select_no_match; - - int num = 0; - int off = 0; - - parse_nth_child_params(selector_param, num, off); - if(!num && !off) return select_no_match; - switch(selector) - { - case pseudo_class_nth_child: - if (!el_parent->is_nth_child(shared_from_this(), num, off, false)) - { - return select_no_match; - } - break; - case pseudo_class_nth_of_type: - if (!el_parent->is_nth_child(shared_from_this(), num, off, true)) - { - return select_no_match; - } - break; - case pseudo_class_nth_last_child: - if (!el_parent->is_nth_last_child(shared_from_this(), num, off, false)) - { - return select_no_match; - } - break; - case pseudo_class_nth_last_of_type: - if (!el_parent->is_nth_last_child(shared_from_this(), num, off, true)) - { - return select_no_match; - } - break; - } - - } - break; - case pseudo_class_not: - { - css_element_selector sel; - sel.parse(selector_param); - if(select(sel, apply_pseudo)) - { - return select_no_match; - } - } - break; - case pseudo_class_lang: - { - trim( selector_param ); - - if( !get_document()->match_lang( selector_param ) ) - { - return select_no_match; - } - } - break; - default: - if(std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), i->val) == m_pseudo_classes.end()) - { - return select_no_match; - } - break; + return select_no_match; } } else { res |= select_match_pseudo_class; } break; + default: + if (select_attribute(attr) == select_no_match) + { + return select_no_match; + } } } return res; } -litehtml::element::ptr litehtml::html_tag::find_ancestor(const css_selector& selector, bool apply_pseudo, bool* is_pseudo) +int litehtml::html_tag::select_pseudoclass(const css_attribute_selector& sel) { element::ptr el_parent = parent(); - if (!el_parent) - { - return nullptr; - } - int res = el_parent->select(selector, apply_pseudo); - if(res != select_no_match) + + switch (sel.name) { - if(is_pseudo) + case _only_child_: + if (!el_parent || !el_parent->is_only_child(shared_from_this(), false)) { - if(res & select_match_pseudo_class) - { - *is_pseudo = true; - } else - { - *is_pseudo = false; - } + return select_no_match; } - return el_parent; - } - return el_parent->find_ancestor(selector, apply_pseudo, is_pseudo); -} - -int litehtml::html_tag::get_floats_height(element_float el_float) const -{ - if(is_floats_holder()) - { - int h = 0; - - bool process = false; - - for(const auto& fb : m_floats_left) + break; + case _only_of_type_: + if (!el_parent || !el_parent->is_only_child(shared_from_this(), true)) { - process = false; - switch(el_float) - { - case float_none: - process = true; - break; - case float_left: - if (fb.clear_floats == clear_left || fb.clear_floats == clear_both) - { - process = true; - } - break; - case float_right: - if (fb.clear_floats == clear_right || fb.clear_floats == clear_both) - { - process = true; - } - break; - } - if(process) - { - if(el_float == float_none) - { - h = std::max(h, fb.pos.bottom()); - } else - { - h = std::max(h, fb.pos.top()); - } - } + return select_no_match; + } + break; + case _first_child_: + if (!el_parent || !el_parent->is_nth_child(shared_from_this(), 0, 1, false)) + { + return select_no_match; + } + break; + case _first_of_type_: + if (!el_parent || !el_parent->is_nth_child(shared_from_this(), 0, 1, true)) + { + return select_no_match; + } + break; + case _last_child_: + if (!el_parent || !el_parent->is_nth_last_child(shared_from_this(), 0, 1, false)) + { + return select_no_match; + } + break; + case _last_of_type_: + if (!el_parent || !el_parent->is_nth_last_child(shared_from_this(), 0, 1, true)) + { + return select_no_match; } + break; + case _nth_child_: + case _nth_of_type_: + case _nth_last_child_: + case _nth_last_of_type_: + { + if (!el_parent) return select_no_match; + int num = sel.a; + int off = sel.b; + if (!num && !off) return select_no_match; - for(const auto fb : m_floats_right) + switch (sel.name) { - process = false; - switch(el_float) + case _nth_child_: + if (!el_parent->is_nth_child(shared_from_this(), num, off, false)) { - case float_none: - process = true; - break; - case float_left: - if (fb.clear_floats == clear_left || fb.clear_floats == clear_both) - { - process = true; - } - break; - case float_right: - if (fb.clear_floats == clear_right || fb.clear_floats == clear_both) - { - process = true; - } - break; + return select_no_match; } - if(process) + break; + case _nth_of_type_: + if (!el_parent->is_nth_child(shared_from_this(), num, off, true)) { - if(el_float == float_none) - { - h = std::max(h, fb.pos.bottom()); - } else - { - h = std::max(h, fb.pos.top()); - } + return select_no_match; } - } - - return h; - } - element::ptr el_parent = parent(); - if (el_parent) - { - int h = el_parent->get_floats_height(el_float); - return h - m_pos.y; - } - return 0; -} - -int litehtml::html_tag::get_left_floats_height() const -{ - if(is_floats_holder()) - { - int h = 0; - if(!m_floats_left.empty()) - { - for (const auto& fb : m_floats_left) + break; + case _nth_last_child_: + if (!el_parent->is_nth_last_child(shared_from_this(), num, off, false)) { - h = std::max(h, fb.pos.bottom()); + return select_no_match; } - } - return h; - } - element::ptr el_parent = parent(); - if (el_parent) - { - int h = el_parent->get_left_floats_height(); - return h - m_pos.y; - } - return 0; -} - -int litehtml::html_tag::get_right_floats_height() const -{ - if(is_floats_holder()) - { - int h = 0; - if(!m_floats_right.empty()) - { - for(const auto& fb : m_floats_right) + break; + case _nth_last_of_type_: + if (!el_parent->is_nth_last_child(shared_from_this(), num, off, true)) { - h = std::max(h, fb.pos.bottom()); + return select_no_match; } + break; } - return h; - } - element::ptr el_parent = parent(); - if (el_parent) - { - int h = el_parent->get_right_floats_height(); - return h - m_pos.y; - } - return 0; -} -int litehtml::html_tag::get_line_left( int y ) -{ - if(is_floats_holder()) - { - if(m_cahe_line_left.is_valid && m_cahe_line_left.hash == y) + } + break; + case _not_: + if (select(*sel.sel, true)) { - return m_cahe_line_left.val; + return select_no_match; } - - int w = 0; - for(const auto& fb : m_floats_left) + break; + case _lang_: + if (!get_document()->match_lang(sel.val)) { - if (y >= fb.pos.top() && y < fb.pos.bottom()) - { - w = std::max(w, fb.pos.right()); - if (w < fb.pos.right()) - { - break; - } - } + return select_no_match; } - m_cahe_line_left.set_value(y, w); - return w; - } - element::ptr el_parent = parent(); - if (el_parent) - { - int w = el_parent->get_line_left(y + m_pos.y); - if (w < 0) + break; + default: + if (std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), sel.name) == m_pseudo_classes.end()) { - w = 0; + return select_no_match; } - return w - (w ? m_pos.x : 0); + break; } - return 0; + return select_match; } -int litehtml::html_tag::get_line_right( int y, int def_right ) +int litehtml::html_tag::select_attribute(const css_attribute_selector& sel) { - if(is_floats_holder()) + const char* attr_value = get_attr(_s(sel.name).c_str()); + + switch (sel.type) { - if(m_cahe_line_right.is_valid && m_cahe_line_right.hash == y) + case select_exists: + if (!attr_value) { - if(m_cahe_line_right.is_default) - { - return def_right; - } else - { - return std::min(m_cahe_line_right.val, def_right); - } + return select_no_match; } - - int w = def_right; - m_cahe_line_right.is_default = true; - for(const auto& fb : m_floats_right) + break; + case select_equal: + if (!attr_value || strcmp(attr_value, sel.val.c_str())) { - if(y >= fb.pos.top() && y < fb.pos.bottom()) - { - w = std::min(w, fb.pos.left()); - m_cahe_line_right.is_default = false; - if(w > fb.pos.left()) - { - break; - } - } + return select_no_match; } - m_cahe_line_right.set_value(y, w); - return w; - } - element::ptr el_parent = parent(); - if (el_parent) - { - int w = el_parent->get_line_right(y + m_pos.y, def_right + m_pos.x); - return w - m_pos.x; - } - return 0; -} - - -void litehtml::html_tag::get_line_left_right( int y, int def_right, int& ln_left, int& ln_right ) -{ - if(is_floats_holder()) - { - ln_left = get_line_left(y); - ln_right = get_line_right(y, def_right); - } else - { - element::ptr el_parent = parent(); - if (el_parent) + break; + case select_contain_str: + if (!attr_value || !strstr(attr_value, sel.val.c_str())) { - el_parent->get_line_left_right(y + m_pos.y, def_right + m_pos.x, ln_left, ln_right); + return select_no_match; } - ln_right -= m_pos.x; - - if(ln_left < 0) - { - ln_left = 0; - } else if (ln_left > 0) - { - ln_left -= m_pos.x; - if (ln_left < 0) - { - ln_left = 0; - } - } - } -} - -int litehtml::html_tag::fix_line_width( int max_width, element_float flt ) -{ - int ret_width = 0; - if(!m_boxes.empty()) - { - elements_vector els; - m_boxes.back()->get_elements(els); - bool was_cleared = false; - if(!els.empty() && els.front()->get_clear() != clear_none) + break; + case select_start_str: + if (!attr_value || strncmp(attr_value, sel.val.c_str(), sel.val.length())) { - if(els.front()->get_clear() == clear_both) - { - was_cleared = true; - } else - { - if( (flt == float_left && els.front()->get_clear() == clear_left) || - (flt == float_right && els.front()->get_clear() == clear_right) ) - { - was_cleared = true; - } - } + return select_no_match; } - - if(!was_cleared) + break; + case select_end_str: + if (!attr_value) { - m_boxes.pop_back(); - - for(elements_vector::iterator i = els.begin(); i != els.end(); i++) - { - int rw = place_element((*i), max_width); - if(rw > ret_width) - { - ret_width = rw; - } - } - } else + return select_no_match; + } + else if (strncmp(attr_value, sel.val.c_str(), sel.val.length())) { - int line_top = 0; - if(m_boxes.back()->get_type() == box_line) - { - line_top = m_boxes.back()->top(); - } else - { - line_top = m_boxes.back()->bottom(); - } - - int line_left = 0; - int line_right = max_width; - get_line_left_right(line_top, max_width, line_left, line_right); - - if(m_boxes.back()->get_type() == box_line) + const char* s = attr_value + strlen(attr_value) - sel.val.length() - 1; + if (s < attr_value) { - if(m_boxes.size() == 1 && m_list_style_type != list_style_type_none && m_list_style_position == list_style_position_inside) - { - int sz_font = get_font_size(); - line_left += sz_font; - } - - if(m_css_text_indent.val() != 0) - { - bool line_box_found = false; - for(box::vector::iterator iter = m_boxes.begin(); iter < m_boxes.end(); iter++) - { - if((*iter)->get_type() == box_line) - { - line_box_found = true; - break; - } - } - if(!line_box_found) - { - line_left += m_css_text_indent.calc_percent(max_width); - } - } - + return select_no_match; } - - elements_vector els; - m_boxes.back()->new_width(line_left, line_right, els); - for(auto& el : els) + if (sel.val != s) { - int rw = place_element(el, max_width); - if(rw > ret_width) - { - ret_width = rw; - } + return select_no_match; } } + break; } - - return ret_width; + return select_match; } -void litehtml::html_tag::add_float(const element::ptr &el, int x, int y) +litehtml::element::ptr litehtml::html_tag::find_ancestor(const css_selector& selector, bool apply_pseudo, bool* is_pseudo) { - if(is_floats_holder()) + element::ptr el_parent = parent(); + if (!el_parent) { - floated_box fb; - fb.pos.x = el->left() + x; - fb.pos.y = el->top() + y; - fb.pos.width = el->width(); - fb.pos.height = el->height(); - fb.float_side = el->get_float(); - fb.clear_floats = el->get_clear(); - fb.el = el; - - if(fb.float_side == float_left) - { - if(m_floats_left.empty()) - { - m_floats_left.push_back(fb); - } else - { - bool inserted = false; - for(floated_box::vector::iterator i = m_floats_left.begin(); i != m_floats_left.end(); i++) - { - if(fb.pos.right() > i->pos.right()) - { - m_floats_left.insert(i, std::move(fb)); - inserted = true; - break; - } - } - if(!inserted) - { - m_floats_left.push_back(std::move(fb)); - } - } - m_cahe_line_left.invalidate(); - } else if(fb.float_side == float_right) + return nullptr; + } + int res = el_parent->select(selector, apply_pseudo); + if(res != select_no_match) + { + if(is_pseudo) { - if(m_floats_right.empty()) + if(res & select_match_pseudo_class) { - m_floats_right.push_back(std::move(fb)); + *is_pseudo = true; } else { - bool inserted = false; - for(floated_box::vector::iterator i = m_floats_right.begin(); i != m_floats_right.end(); i++) - { - if(fb.pos.left() < i->pos.left()) - { - m_floats_right.insert(i, std::move(fb)); - inserted = true; - break; - } - } - if(!inserted) - { - m_floats_right.push_back(fb); - } + *is_pseudo = false; } - m_cahe_line_right.invalidate(); - } - } else - { - element::ptr el_parent = parent(); - if (el_parent) - { - el_parent->add_float(el, x + m_pos.x, y + m_pos.y); } + return el_parent; } + return el_parent->find_ancestor(selector, apply_pseudo, is_pseudo); } -int litehtml::html_tag::find_next_line_top( int top, int width, int def_right ) +void litehtml::html_tag::parse_attributes() { - if(is_floats_holder()) + for(auto& el : m_children) { - int new_top = top; - int_vector points; - - for(const auto& fb : m_floats_left) - { - if(fb.pos.top() >= top) - { - if(find(points.begin(), points.end(), fb.pos.top()) == points.end()) - { - points.push_back(fb.pos.top()); - } - } - if (fb.pos.bottom() >= top) - { - if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end()) - { - points.push_back(fb.pos.bottom()); - } - } - } - - for (const auto& fb : m_floats_right) - { - if (fb.pos.top() >= top) - { - if (find(points.begin(), points.end(), fb.pos.top()) == points.end()) - { - points.push_back(fb.pos.top()); - } - } - if (fb.pos.bottom() >= top) - { - if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end()) - { - points.push_back(fb.pos.bottom()); - } - } - } - - if(!points.empty()) - { - sort(points.begin(), points.end(), std::less<int>( )); - new_top = points.back(); - - for(auto pt : points) - { - int pos_left = 0; - int pos_right = def_right; - get_line_left_right(pt, def_right, pos_left, pos_right); - - if(pos_right - pos_left >= width) - { - new_top = pt; - break; - } - } - } - return new_top; + el->parse_attributes(); } - element::ptr el_parent = parent(); - if (el_parent) +} + +void litehtml::html_tag::get_text( string& text ) +{ + for (auto& el : m_children) { - int new_top = el_parent->find_next_line_top(top + m_pos.y, width, def_right + m_pos.x); - return new_top - m_pos.y; + el->get_text(text); } - return 0; } -void litehtml::html_tag::parse_background() +bool litehtml::html_tag::is_body() const { - // parse background-color - m_bg.m_color = get_color(_t("background-color"), false, web_color(0, 0, 0, 0)); - - // parse background-position - const tchar_t* str = get_style_property(_t("background-position"), false, _t("0% 0%")); - if(str) - { - string_vector res; - split_string(str, res, _t(" \t")); - if(res.size() > 0) - { - if(res.size() == 1) - { - if( value_in_list(res[0].c_str(), _t("left;right;center")) ) - { - m_bg.m_position.x.fromString(res[0], _t("left;right;center")); - m_bg.m_position.y.set_value(50, css_units_percentage); - } else if( value_in_list(res[0].c_str(), _t("top;bottom;center")) ) - { - m_bg.m_position.y.fromString(res[0], _t("top;bottom;center")); - m_bg.m_position.x.set_value(50, css_units_percentage); - } else - { - m_bg.m_position.x.fromString(res[0], _t("left;right;center")); - m_bg.m_position.y.set_value(50, css_units_percentage); - } - } else - { - if(value_in_list(res[0].c_str(), _t("left;right"))) - { - m_bg.m_position.x.fromString(res[0], _t("left;right;center")); - m_bg.m_position.y.fromString(res[1], _t("top;bottom;center")); - } else if(value_in_list(res[0].c_str(), _t("top;bottom"))) - { - m_bg.m_position.x.fromString(res[1], _t("left;right;center")); - m_bg.m_position.y.fromString(res[0], _t("top;bottom;center")); - } else if(value_in_list(res[1].c_str(), _t("left;right"))) - { - m_bg.m_position.x.fromString(res[1], _t("left;right;center")); - m_bg.m_position.y.fromString(res[0], _t("top;bottom;center")); - }else if(value_in_list(res[1].c_str(), _t("top;bottom"))) - { - m_bg.m_position.x.fromString(res[0], _t("left;right;center")); - m_bg.m_position.y.fromString(res[1], _t("top;bottom;center")); - } else - { - m_bg.m_position.x.fromString(res[0], _t("left;right;center")); - m_bg.m_position.y.fromString(res[1], _t("top;bottom;center")); - } - } - - if(m_bg.m_position.x.is_predefined()) - { - switch(m_bg.m_position.x.predef()) - { - case 0: - m_bg.m_position.x.set_value(0, css_units_percentage); - break; - case 1: - m_bg.m_position.x.set_value(100, css_units_percentage); - break; - case 2: - m_bg.m_position.x.set_value(50, css_units_percentage); - break; - } - } - if(m_bg.m_position.y.is_predefined()) - { - switch(m_bg.m_position.y.predef()) - { - case 0: - m_bg.m_position.y.set_value(0, css_units_percentage); - break; - case 1: - m_bg.m_position.y.set_value(100, css_units_percentage); - break; - case 2: - m_bg.m_position.y.set_value(50, css_units_percentage); - break; - } - } - } else - { - m_bg.m_position.x.set_value(0, css_units_percentage); - m_bg.m_position.y.set_value(0, css_units_percentage); - } - } else - { - m_bg.m_position.y.set_value(0, css_units_percentage); - m_bg.m_position.x.set_value(0, css_units_percentage); - } - - str = get_style_property(_t("background-size"), false, _t("auto")); - if(str) - { - string_vector res; - split_string(str, res, _t(" \t")); - if(!res.empty()) - { - m_bg.m_position.width.fromString(res[0], background_size_strings); - if(res.size() > 1) - { - m_bg.m_position.height.fromString(res[1], background_size_strings); - } else - { - m_bg.m_position.height.predef(background_size_auto); - } - } else - { - m_bg.m_position.width.predef(background_size_auto); - m_bg.m_position.height.predef(background_size_auto); - } - } - - document::ptr doc = get_document(); - - doc->cvt_units(m_bg.m_position.x, m_font_size); - doc->cvt_units(m_bg.m_position.y, m_font_size); - doc->cvt_units(m_bg.m_position.width, m_font_size); - doc->cvt_units(m_bg.m_position.height, m_font_size); - - // parse background_attachment - m_bg.m_attachment = (background_attachment) value_index( - get_style_property(_t("background-attachment"), false, _t("scroll")), - background_attachment_strings, - background_attachment_scroll); - - // parse background_attachment - m_bg.m_repeat = (background_repeat) value_index( - get_style_property(_t("background-repeat"), false, _t("repeat")), - background_repeat_strings, - background_repeat_repeat); - - // parse background_clip - m_bg.m_clip = (background_box) value_index( - get_style_property(_t("background-clip"), false, _t("border-box")), - background_box_strings, - background_box_border); - - // parse background_origin - m_bg.m_origin = (background_box) value_index( - get_style_property(_t("background-origin"), false, _t("padding-box")), - background_box_strings, - background_box_content); - - // parse background-image - css::parse_css_url(get_style_property(_t("background-image"), false, _t("")), m_bg.m_image); - m_bg.m_baseurl = get_style_property(_t("background-image-baseurl"), false, _t("")); - - if(!m_bg.m_image.empty()) - { - doc->container()->load_image(m_bg.m_image.c_str(), m_bg.m_baseurl.empty() ? 0 : m_bg.m_baseurl.c_str(), true); - } -} - -void litehtml::html_tag::add_positioned(const element::ptr &el) -{ - if (m_el_position != element_position_static || (!have_parent())) - { - m_positioned.push_back(el); - } else - { - element::ptr el_parent = parent(); - if (el_parent) - { - el_parent->add_positioned(el); - } - } -} - -void litehtml::html_tag::calc_outlines( int parent_width ) -{ - m_padding.left = m_css_padding.left.calc_percent(parent_width); - m_padding.right = m_css_padding.right.calc_percent(parent_width); - - m_borders.left = m_css_borders.left.width.calc_percent(parent_width); - m_borders.right = m_css_borders.right.width.calc_percent(parent_width); - - m_margins.left = m_css_margins.left.calc_percent(parent_width); - m_margins.right = m_css_margins.right.calc_percent(parent_width); - - m_margins.top = m_css_margins.top.calc_percent(parent_width); - m_margins.bottom = m_css_margins.bottom.calc_percent(parent_width); - - m_padding.top = m_css_padding.top.calc_percent(parent_width); - m_padding.bottom = m_css_padding.bottom.calc_percent(parent_width); -} - -void litehtml::html_tag::calc_auto_margins(int parent_width) -{ - if (get_element_position() != element_position_absolute && (m_display == display_block || m_display == display_table)) - { - if (m_css_margins.left.is_predefined() && m_css_margins.right.is_predefined()) - { - int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right; - if (el_width <= parent_width) - { - m_margins.left = (parent_width - el_width) / 2; - m_margins.right = (parent_width - el_width) - m_margins.left; - } - else - { - m_margins.left = 0; - m_margins.right = 0; - } - } - else if (m_css_margins.left.is_predefined() && !m_css_margins.right.is_predefined()) - { - int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.right; - m_margins.left = parent_width - el_width; - if (m_margins.left < 0) m_margins.left = 0; - } - else if (!m_css_margins.left.is_predefined() && m_css_margins.right.is_predefined()) - { - int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.left; - m_margins.right = parent_width - el_width; - if (m_margins.right < 0) m_margins.right = 0; - } - } -} - -void litehtml::html_tag::parse_attributes() -{ - for(auto& el : m_children) - { - el->parse_attributes(); - } -} - -void litehtml::html_tag::get_text( tstring& text ) -{ - for (auto& el : m_children) - { - el->get_text(text); - } -} - -bool litehtml::html_tag::is_body() const -{ - return false; -} - -void litehtml::html_tag::set_data( const tchar_t* data ) -{ - -} - -void litehtml::html_tag::get_inline_boxes( position::vector& boxes ) -{ - litehtml::box* old_box = 0; - position pos; - for(auto& el : m_children) - { - if(!el->skip()) - { - if(el->m_box) - { - if(el->m_box != old_box) - { - if(old_box) - { - if(boxes.empty()) - { - pos.x -= m_padding.left + m_borders.left; - pos.width += m_padding.left + m_borders.left; - } - boxes.push_back(pos); - } - old_box = el->m_box; - pos.x = el->left() + el->margin_left(); - pos.y = el->top() - m_padding.top - m_borders.top; - pos.width = 0; - pos.height = 0; - } - pos.width = el->right() - pos.x - el->margin_right() - el->margin_left(); - pos.height = std::max(pos.height, el->height() + m_padding.top + m_padding.bottom + m_borders.top + m_borders.bottom); - } else if(el->get_display() == display_inline) - { - position::vector sub_boxes; - el->get_inline_boxes(sub_boxes); - if(!sub_boxes.empty()) - { - sub_boxes.rbegin()->width += el->margin_right(); - if(boxes.empty()) - { - if(m_padding.left + m_borders.left > 0) - { - position padding_box = (*sub_boxes.begin()); - padding_box.x -= m_padding.left + m_borders.left + el->margin_left(); - padding_box.width = m_padding.left + m_borders.left + el->margin_left(); - boxes.push_back(padding_box); - } - } - - sub_boxes.rbegin()->width += el->margin_right(); - - boxes.insert(boxes.end(), sub_boxes.begin(), sub_boxes.end()); - } - } - } - } - if(pos.width || pos.height) - { - if(boxes.empty()) - { - pos.x -= m_padding.left + m_borders.left; - pos.width += m_padding.left + m_borders.left; - } - boxes.push_back(pos); - } - if(!boxes.empty()) - { - if(m_padding.right + m_borders.right > 0) - { - boxes.back().width += m_padding.right + m_borders.right; - } - } -} - -bool litehtml::html_tag::on_mouse_over() -{ - bool ret = false; - - element::ptr el = shared_from_this(); - while(el) - { - if(el->set_pseudo_class(_t("hover"), true)) - { - ret = true; - } - el = el->parent(); - } - - return ret; -} - -bool litehtml::html_tag::find_styles_changes( position::vector& redraw_boxes, int x, int y ) -{ - if(m_display == display_inline_text) - { - return false; - } - - bool ret = false; - bool apply = false; - for (used_selector::vector::iterator iter = m_used_styles.begin(); iter != m_used_styles.end() && !apply; iter++) - { - if((*iter)->m_selector->is_media_valid()) - { - int res = select(*((*iter)->m_selector), true); - if( (res == select_no_match && (*iter)->m_used) || (res == select_match && !(*iter)->m_used) ) - { - apply = true; - } - } - } - - if(apply) - { - if(m_display == display_inline || m_display == display_table_row) - { - position::vector boxes; - get_inline_boxes(boxes); - for(position::vector::iterator pos = boxes.begin(); pos != boxes.end(); pos++) - { - pos->x += x; - pos->y += y; - redraw_boxes.push_back(*pos); - } - } else - { - position pos = m_pos; - if(m_el_position != element_position_fixed) - { - pos.x += x; - pos.y += y; - } - pos += m_padding; - pos += m_borders; - redraw_boxes.push_back(pos); - } - - ret = true; - refresh_styles(); - parse_styles(); - } - for (auto& el : m_children) - { - if(!el->skip()) - { - if(m_el_position != element_position_fixed) - { - if(el->find_styles_changes(redraw_boxes, x + m_pos.x, y + m_pos.y)) - { - ret = true; - } - } else - { - if(el->find_styles_changes(redraw_boxes, m_pos.x, m_pos.y)) - { - ret = true; - } - } - } - } - return ret; -} - -bool litehtml::html_tag::on_mouse_leave() -{ - bool ret = false; - - element::ptr el = shared_from_this(); - while(el) - { - if(el->set_pseudo_class(_t("hover"), false)) - { - ret = true; - } - if(el->set_pseudo_class(_t("active"), false)) - { - ret = true; - } - el = el->parent(); - } - - return ret; -} - -bool litehtml::html_tag::on_lbutton_down() -{ - bool ret = false; - - element::ptr el = shared_from_this(); - while (el) - { - if (el->set_pseudo_class(_t("active"), true)) - { - ret = true; - } - el = el->parent(); - } - - return ret; -} - -bool litehtml::html_tag::on_lbutton_up() -{ - bool ret = false; - - element::ptr el = shared_from_this(); - while (el) - { - if (el->set_pseudo_class(_t("active"), false)) - { - ret = true; - } - el = el->parent(); - } - - on_click(); - - return ret; -} - -void litehtml::html_tag::on_click() -{ - if (have_parent()) - { - element::ptr el_parent = parent(); - if (el_parent) - { - el_parent->on_click(); - } - } -} - -const litehtml::tchar_t* litehtml::html_tag::get_cursor() -{ - return get_style_property(_t("cursor"), true, 0); -} - -static const int font_size_table[8][7] = -{ - { 9, 9, 9, 9, 11, 14, 18}, - { 9, 9, 9, 10, 12, 15, 20}, - { 9, 9, 9, 11, 13, 17, 22}, - { 9, 9, 10, 12, 14, 18, 24}, - { 9, 9, 10, 13, 16, 20, 26}, - { 9, 9, 11, 14, 17, 21, 28}, - { 9, 10, 12, 15, 17, 23, 30}, - { 9, 10, 13, 16, 18, 24, 32} -}; - - -void litehtml::html_tag::init_font() -{ - // initialize font size - const tchar_t* str = get_style_property(_t("font-size"), false, 0); - - int parent_sz = 0; - int doc_font_size = get_document()->container()->get_default_font_size(); - element::ptr el_parent = parent(); - if (el_parent) - { - parent_sz = el_parent->get_font_size(); - } else - { - parent_sz = doc_font_size; - } - - - if(!str) - { - m_font_size = parent_sz; - } else - { - m_font_size = parent_sz; - - css_length sz; - sz.fromString(str, font_size_strings); - if(sz.is_predefined()) - { - int idx_in_table = doc_font_size - 9; - if(idx_in_table >= 0 && idx_in_table <= 7) - { - if(sz.predef() >= fontSize_xx_small && sz.predef() <= fontSize_xx_large) - { - m_font_size = font_size_table[idx_in_table][sz.predef()]; - } else - { - m_font_size = doc_font_size; - } - } else - { - switch(sz.predef()) - { - case fontSize_xx_small: - m_font_size = doc_font_size * 3 / 5; - break; - case fontSize_x_small: - m_font_size = doc_font_size * 3 / 4; - break; - case fontSize_small: - m_font_size = doc_font_size * 8 / 9; - break; - case fontSize_large: - m_font_size = doc_font_size * 6 / 5; - break; - case fontSize_x_large: - m_font_size = doc_font_size * 3 / 2; - break; - case fontSize_xx_large: - m_font_size = doc_font_size * 2; - break; - default: - m_font_size = doc_font_size; - break; - } - } - } else - { - if(sz.units() == css_units_percentage) - { - m_font_size = sz.calc_percent(parent_sz); - } else if(sz.units() == css_units_none) - { - m_font_size = parent_sz; - } else - { - m_font_size = get_document()->cvt_units(sz, parent_sz); - } - } - } - - // initialize font - const tchar_t* name = get_style_property(_t("font-family"), true, _t("inherit")); - const tchar_t* weight = get_style_property(_t("font-weight"), true, _t("normal")); - const tchar_t* style = get_style_property(_t("font-style"), true, _t("normal")); - const tchar_t* decoration = get_style_property(_t("text-decoration"), true, _t("none")); - - m_font = get_document()->get_font(name, m_font_size, weight, style, decoration, &m_font_metrics); -} - -bool litehtml::html_tag::is_break() const -{ - return false; -} - -void litehtml::html_tag::set_tagName( const tchar_t* tag ) -{ - tstring s_val = tag; - for(size_t i = 0; i < s_val.length(); i++) - { - s_val[i] = std::tolower(s_val[i], std::locale::classic()); - } - m_tag = s_val; -} - -void litehtml::html_tag::draw_background( uint_ptr hdc, int x, int y, const position* clip ) -{ - position pos = m_pos; - pos.x += x; - pos.y += y; - - position el_pos = pos; - el_pos += m_padding; - el_pos += m_borders; - - if(m_display != display_inline && m_display != display_table_row) - { - if(el_pos.does_intersect(clip)) - { - const background* bg = get_background(); - if(bg) - { - background_paint bg_paint; - init_background_paint(pos, bg_paint, bg); - - get_document()->container()->draw_background(hdc, bg_paint); - } - position border_box = pos; - border_box += m_padding; - border_box += m_borders; - - borders bdr = m_css_borders; - bdr.radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height); - - get_document()->container()->draw_borders(hdc, bdr, border_box, have_parent() ? false : true); - } - } else - { - const background* bg = get_background(); - - position::vector boxes; - get_inline_boxes(boxes); - - background_paint bg_paint; - position content_box; - - for(position::vector::iterator box = boxes.begin(); box != boxes.end(); box++) - { - box->x += x; - box->y += y; - - if(box->does_intersect(clip)) - { - content_box = *box; - content_box -= m_borders; - content_box -= m_padding; - - if(bg) - { - init_background_paint(content_box, bg_paint, bg); - } - - css_borders bdr; - - // set left borders radius for the first box - if(box == boxes.begin()) - { - bdr.radius.bottom_left_x = m_css_borders.radius.bottom_left_x; - bdr.radius.bottom_left_y = m_css_borders.radius.bottom_left_y; - bdr.radius.top_left_x = m_css_borders.radius.top_left_x; - bdr.radius.top_left_y = m_css_borders.radius.top_left_y; - } - - // set right borders radius for the last box - if(box == boxes.end() - 1) - { - bdr.radius.bottom_right_x = m_css_borders.radius.bottom_right_x; - bdr.radius.bottom_right_y = m_css_borders.radius.bottom_right_y; - bdr.radius.top_right_x = m_css_borders.radius.top_right_x; - bdr.radius.top_right_y = m_css_borders.radius.top_right_y; - } - - - bdr.top = m_css_borders.top; - bdr.bottom = m_css_borders.bottom; - if(box == boxes.begin()) - { - bdr.left = m_css_borders.left; - } - if(box == boxes.end() - 1) - { - bdr.right = m_css_borders.right; - } - - - if(bg) - { - bg_paint.border_radius = bdr.radius.calc_percents(bg_paint.border_box.width, bg_paint.border_box.width); - get_document()->container()->draw_background(hdc, bg_paint); - } - borders b = bdr; - b.radius = bdr.radius.calc_percents(box->width, box->height); - get_document()->container()->draw_borders(hdc, b, *box, false); - } - } - } -} - -int litehtml::html_tag::render_inline(const element::ptr &container, int max_width) -{ - int ret_width = 0; - int rw = 0; - - white_space ws = get_white_space(); - bool skip_spaces = false; - if (ws == white_space_normal || - ws == white_space_nowrap || - ws == white_space_pre_line) - { - skip_spaces = true; - } - bool was_space = false; - - for (auto& el : m_children) - { - // skip spaces to make rendering a bit faster - if (skip_spaces) - { - if (el->is_white_space()) - { - if (was_space) - { - el->skip(true); - continue; - } - else - { - was_space = true; - } - } - else - { - was_space = false; - } - } - - rw = container->place_element( el, max_width ); - if(rw > ret_width) - { - ret_width = rw; - } - } - return ret_width; -} - -int litehtml::html_tag::place_element(const element::ptr &el, int max_width) -{ - if(el->get_display() == display_none) return 0; - - if(el->get_display() == display_inline) - { - return el->render_inline(shared_from_this(), max_width); - } - - element_position el_position = el->get_element_position(); - - if(el_position == element_position_absolute || el_position == element_position_fixed) - { - int line_top = 0; - if(!m_boxes.empty()) - { - if(m_boxes.back()->get_type() == box_line) - { - line_top = m_boxes.back()->top(); - if(!m_boxes.back()->is_empty()) - { - line_top += line_height(); - } - } else - { - line_top = m_boxes.back()->bottom(); - } - } - - el->render(0, line_top, max_width); - el->m_pos.x += el->content_margins_left(); - el->m_pos.y += el->content_margins_top(); - - return 0; - } - - int ret_width = 0; - - switch(el->get_float()) - { - case float_left: - { - int line_top = 0; - if(!m_boxes.empty()) - { - if(m_boxes.back()->get_type() == box_line) - { - line_top = m_boxes.back()->top(); - } else - { - line_top = m_boxes.back()->bottom(); - } - } - line_top = get_cleared_top(el, line_top); - int line_left = 0; - int line_right = max_width; - get_line_left_right(line_top, max_width, line_left, line_right); - - el->render(line_left, line_top, line_right); - if(el->right() > line_right) - { - int new_top = find_next_line_top(el->top(), el->width(), max_width); - el->m_pos.x = get_line_left(new_top) + el->content_margins_left(); - el->m_pos.y = new_top + el->content_margins_top(); - } - add_float(el, 0, 0); - ret_width = fix_line_width(max_width, float_left); - if(!ret_width) - { - ret_width = el->right(); - } - } - break; - case float_right: - { - int line_top = 0; - if(!m_boxes.empty()) - { - if(m_boxes.back()->get_type() == box_line) - { - line_top = m_boxes.back()->top(); - } else - { - line_top = m_boxes.back()->bottom(); - } - } - line_top = get_cleared_top(el, line_top); - int line_left = 0; - int line_right = max_width; - get_line_left_right(line_top, max_width, line_left, line_right); - - el->render(0, line_top, line_right); - - if(line_left + el->width() > line_right) - { - int new_top = find_next_line_top(el->top(), el->width(), max_width); - el->m_pos.x = get_line_right(new_top, max_width) - el->width() + el->content_margins_left(); - el->m_pos.y = new_top + el->content_margins_top(); - } else - { - el->m_pos.x = line_right - el->width() + el->content_margins_left(); - } - add_float(el, 0, 0); - ret_width = fix_line_width(max_width, float_right); - - if(!ret_width) - { - line_left = 0; - line_right = max_width; - get_line_left_right(line_top, max_width, line_left, line_right); - - ret_width = ret_width + (max_width - line_right); - } - } - break; - default: - { - line_context line_ctx; - line_ctx.top = 0; - if (!m_boxes.empty()) - { - line_ctx.top = m_boxes.back()->top(); - } - line_ctx.left = 0; - line_ctx.right = max_width; - line_ctx.fix_top(); - get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right); - - switch(el->get_display()) - { - case display_inline_block: - case display_inline_table: - ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.right); - break; - case display_block: - if(el->is_replaced() || el->is_floats_holder()) - { - element::ptr el_parent = el->parent(); - el->m_pos.width = el->get_css_width().calc_percent(line_ctx.right - line_ctx.left); - el->m_pos.height = el->get_css_height().calc_percent(el_parent ? el_parent->m_pos.height : 0); - } - el->calc_outlines(line_ctx.right - line_ctx.left); - break; - case display_inline_text: - { - litehtml::size sz; - el->get_content_size(sz, line_ctx.right); - el->m_pos = sz; - } - break; - default: - ret_width = 0; - break; - } - - bool add_box = true; - if(!m_boxes.empty()) - { - if(m_boxes.back()->can_hold(el, m_white_space)) - { - add_box = false; - } - } - if(add_box) - { - new_box(el, max_width, line_ctx); - } else if(!m_boxes.empty()) - { - line_ctx.top = m_boxes.back()->top(); - } - - if (line_ctx.top != line_ctx.calculatedTop) - { - line_ctx.left = 0; - line_ctx.right = max_width; - line_ctx.fix_top(); - get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right); - } - - if(!el->is_inline_box()) - { - if(m_boxes.size() == 1) - { - if(collapse_top_margin()) - { - int shift = el->margin_top(); - if(shift >= 0) - { - line_ctx.top -= shift; - m_boxes.back()->y_shift(-shift); - } - } - } else - { - int shift = 0; - int prev_margin = m_boxes[m_boxes.size() - 2]->bottom_margin(); - - if(prev_margin > el->margin_top()) - { - shift = el->margin_top(); - } else - { - shift = prev_margin; - } - if(shift >= 0) - { - line_ctx.top -= shift; - m_boxes.back()->y_shift(-shift); - } - } - } - - switch(el->get_display()) - { - case display_table: - case display_list_item: - ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.width()); - break; - case display_block: - case display_table_cell: - case display_table_caption: - case display_table_row: - if(el->is_replaced() || el->is_floats_holder()) - { - ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.width()) + line_ctx.left + (max_width - line_ctx.right); - } else - { - ret_width = el->render(0, line_ctx.top, max_width); - } - break; - default: - ret_width = 0; - break; - } - - m_boxes.back()->add_element(el); - - if(el->is_inline_box() && !el->skip()) - { - ret_width = el->right() + (max_width - line_ctx.right); - } - } - break; - } - - return ret_width; -} - -bool litehtml::html_tag::set_pseudo_class( const tchar_t* pclass, bool add ) -{ - bool ret = false; - if(add) - { - if(std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), pclass) == m_pseudo_classes.end()) - { - m_pseudo_classes.push_back(pclass); - ret = true; - } - } else - { - string_vector::iterator pi = std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), pclass); - if(pi != m_pseudo_classes.end()) - { - m_pseudo_classes.erase(pi); - ret = true; - } - } - return ret; -} - -bool litehtml::html_tag::set_class( const tchar_t* pclass, bool add ) -{ - string_vector classes; - bool changed = false; - - split_string( pclass, classes, _t(" ") ); - - if(add) - { - for( auto & _class : classes ) - { - if(std::find(m_class_values.begin(), m_class_values.end(), _class) == m_class_values.end()) - { - m_class_values.push_back( std::move( _class ) ); - changed = true; - } - } - } else - { - for( const auto & _class : classes ) - { - auto end = std::remove(m_class_values.begin(), m_class_values.end(), _class); - - if(end != m_class_values.end()) - { - m_class_values.erase(end, m_class_values.end()); - changed = true; - } - } - } - - if( changed ) - { - tstring class_string; - join_string(class_string, m_class_values, _t(" ")); - set_attr(_t("class"), class_string.c_str()); - - return true; - } - else - { - return false; - } - -} - -int litehtml::html_tag::line_height() const -{ - return m_line_height; -} - -bool litehtml::html_tag::is_replaced() const -{ - return false; -} - -int litehtml::html_tag::finish_last_box(bool end_of_render) -{ - int line_top = 0; - - if(!m_boxes.empty()) - { - m_boxes.back()->finish(end_of_render); - - if(m_boxes.back()->is_empty()) - { - line_top = m_boxes.back()->top(); - m_boxes.pop_back(); - } - - if(!m_boxes.empty()) - { - line_top = m_boxes.back()->bottom(); - } - } - return line_top; -} - -int litehtml::html_tag::new_box(const element::ptr &el, int max_width, line_context& line_ctx) -{ - line_ctx.top = get_cleared_top(el, finish_last_box()); - - line_ctx.left = 0; - line_ctx.right = max_width; - line_ctx.fix_top(); - get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right); - - if(el->is_inline_box() || el->is_floats_holder()) - { - if (el->width() > line_ctx.right - line_ctx.left) - { - line_ctx.top = find_next_line_top(line_ctx.top, el->width(), max_width); - line_ctx.left = 0; - line_ctx.right = max_width; - line_ctx.fix_top(); - get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right); - } - } - - int first_line_margin = 0; - if(m_boxes.empty() && m_list_style_type != list_style_type_none && m_list_style_position == list_style_position_inside) - { - int sz_font = get_font_size(); - first_line_margin = sz_font; - } - - if(el->is_inline_box()) - { - int text_indent = 0; - if(m_css_text_indent.val() != 0) - { - bool line_box_found = false; - for(box::vector::iterator iter = m_boxes.begin(); iter != m_boxes.end(); iter++) - { - if((*iter)->get_type() == box_line) - { - line_box_found = true; - break; - } - } - if(!line_box_found) - { - text_indent = m_css_text_indent.calc_percent(max_width); - } - } - - font_metrics fm; - get_font(&fm); - m_boxes.emplace_back(std::unique_ptr<line_box>(new line_box(line_ctx.top, line_ctx.left + first_line_margin + text_indent, line_ctx.right, line_height(), fm, m_text_align))); - } else - { - m_boxes.emplace_back(std::unique_ptr<block_box>(new block_box(line_ctx.top, line_ctx.left, line_ctx.right))); - } - - return line_ctx.top; -} - -int litehtml::html_tag::get_cleared_top(const element::ptr &el, int line_top) const -{ - switch(el->get_clear()) - { - case clear_left: - { - int fh = get_left_floats_height(); - if(fh && fh > line_top) - { - line_top = fh; - } - } - break; - case clear_right: - { - int fh = get_right_floats_height(); - if(fh && fh > line_top) - { - line_top = fh; - } - } - break; - case clear_both: - { - int fh = get_floats_height(); - if(fh && fh > line_top) - { - line_top = fh; - } - } - break; - default: - if(el->get_float() != float_none) - { - int fh = get_floats_height(el->get_float()); - if(fh && fh > line_top) - { - line_top = fh; - } - } - break; - } - return line_top; -} - -litehtml::style_display litehtml::html_tag::get_display() const -{ - return m_display; -} - -litehtml::element_float litehtml::html_tag::get_float() const -{ - return m_float; -} - -bool litehtml::html_tag::is_floats_holder() const -{ - if( m_display == display_inline_block || - m_display == display_table_cell || - !have_parent() || - is_body() || - m_float != float_none || - m_el_position == element_position_absolute || - m_el_position == element_position_fixed || - m_overflow > overflow_visible) - { - return true; - } - return false; -} - -bool litehtml::html_tag::is_first_child_inline(const element::ptr& el) const -{ - if(!m_children.empty()) - { - for (const auto& this_el : m_children) - { - if (!this_el->is_white_space()) - { - if (el == this_el) - { - return true; - } - if (this_el->get_display() == display_inline) - { - if (this_el->have_inline_child()) - { - return false; - } - } else - { - return false; - } - } - } - } - return false; -} - -bool litehtml::html_tag::is_last_child_inline(const element::ptr& el) -{ - if(!m_children.empty()) - { - for (auto this_el = m_children.rbegin(); this_el < m_children.rend(); ++this_el) - { - if (!(*this_el)->is_white_space()) - { - if (el == (*this_el)) - { - return true; - } - if ((*this_el)->get_display() == display_inline) - { - if ((*this_el)->have_inline_child()) - { - return false; - } - } else - { - return false; - } - } - } - } - return false; -} - -litehtml::white_space litehtml::html_tag::get_white_space() const -{ - return m_white_space; -} - -litehtml::vertical_align litehtml::html_tag::get_vertical_align() const -{ - return m_vertical_align; -} - -litehtml::css_length litehtml::html_tag::get_css_left() const -{ - return m_css_offsets.left; -} - -litehtml::css_length litehtml::html_tag::get_css_right() const -{ - return m_css_offsets.right; -} - -litehtml::css_length litehtml::html_tag::get_css_top() const -{ - return m_css_offsets.top; -} - -litehtml::css_length litehtml::html_tag::get_css_bottom() const -{ - return m_css_offsets.bottom; -} - - -litehtml::css_offsets litehtml::html_tag::get_css_offsets() const -{ - return m_css_offsets; -} - -litehtml::element_clear litehtml::html_tag::get_clear() const -{ - return m_clear; -} - -litehtml::css_length litehtml::html_tag::get_css_width() const -{ - return m_css_width; -} - -litehtml::css_length litehtml::html_tag::get_css_height() const -{ - return m_css_height; -} - -size_t litehtml::html_tag::get_children_count() const -{ - return m_children.size(); -} - -litehtml::element::ptr litehtml::html_tag::get_child( int idx ) const -{ - return m_children[idx]; -} - -void litehtml::html_tag::set_css_width( css_length& w ) -{ - m_css_width = w; -} - -void litehtml::html_tag::apply_vertical_align() -{ - if(!m_boxes.empty()) - { - int add = 0; - int content_height = m_boxes.back()->bottom(); - - if(m_pos.height > content_height) - { - switch(m_vertical_align) - { - case va_middle: - add = (m_pos.height - content_height) / 2; - break; - case va_bottom: - add = m_pos.height - content_height; - break; - default: - add = 0; - break; - } - } - - if(add) - { - for(size_t i = 0; i < m_boxes.size(); i++) - { - m_boxes[i]->y_shift(add); - } - } - } -} - -litehtml::element_position litehtml::html_tag::get_element_position(css_offsets* offsets) const -{ - if(offsets && m_el_position != element_position_static) - { - *offsets = m_css_offsets; - } - return m_el_position; -} - -void litehtml::html_tag::init_background_paint(position pos, background_paint &bg_paint, const background* bg) -{ - if(!bg) return; - - bg_paint = *bg; - position content_box = pos; - position padding_box = pos; - padding_box += m_padding; - position border_box = padding_box; - border_box += m_borders; - - switch(bg->m_clip) - { - case litehtml::background_box_padding: - bg_paint.clip_box = padding_box; - break; - case litehtml::background_box_content: - bg_paint.clip_box = content_box; - break; - default: - bg_paint.clip_box = border_box; - break; - } - - switch(bg->m_origin) - { - case litehtml::background_box_border: - bg_paint.origin_box = border_box; - break; - case litehtml::background_box_content: - bg_paint.origin_box = content_box; - break; - default: - bg_paint.origin_box = padding_box; - break; - } - - if(!bg_paint.image.empty()) - { - get_document()->container()->get_image_size(bg_paint.image.c_str(), bg_paint.baseurl.c_str(), bg_paint.image_size); - if(bg_paint.image_size.width && bg_paint.image_size.height) - { - litehtml::size img_new_sz = bg_paint.image_size; - double img_ar_width = (double) bg_paint.image_size.width / (double) bg_paint.image_size.height; - double img_ar_height = (double) bg_paint.image_size.height / (double) bg_paint.image_size.width; - - - if(bg->m_position.width.is_predefined()) - { - switch(bg->m_position.width.predef()) - { - case litehtml::background_size_contain: - if( (int) ((double) bg_paint.origin_box.width * img_ar_height) <= bg_paint.origin_box.height ) - { - img_new_sz.width = bg_paint.origin_box.width; - img_new_sz.height = (int) ((double) bg_paint.origin_box.width * img_ar_height); - } else - { - img_new_sz.height = bg_paint.origin_box.height; - img_new_sz.width = (int) ((double) bg_paint.origin_box.height * img_ar_width); - } - break; - case litehtml::background_size_cover: - if( (int) ((double) bg_paint.origin_box.width * img_ar_height) >= bg_paint.origin_box.height ) - { - img_new_sz.width = bg_paint.origin_box.width; - img_new_sz.height = (int) ((double) bg_paint.origin_box.width * img_ar_height); - } else - { - img_new_sz.height = bg_paint.origin_box.height; - img_new_sz.width = (int) ((double) bg_paint.origin_box.height * img_ar_width); - } - break; - break; - case litehtml::background_size_auto: - if(!bg->m_position.height.is_predefined()) - { - img_new_sz.height = bg->m_position.height.calc_percent(bg_paint.origin_box.height); - img_new_sz.width = (int) ((double) img_new_sz.height * img_ar_width); - } - break; - } - } else - { - img_new_sz.width = bg->m_position.width.calc_percent(bg_paint.origin_box.width); - if(bg->m_position.height.is_predefined()) - { - img_new_sz.height = (int) ((double) img_new_sz.width * img_ar_height); - } else - { - img_new_sz.height = bg->m_position.height.calc_percent(bg_paint.origin_box.height); - } - } - - bg_paint.image_size = img_new_sz; - bg_paint.position_x = bg_paint.origin_box.x + (int) bg->m_position.x.calc_percent(bg_paint.origin_box.width - bg_paint.image_size.width); - bg_paint.position_y = bg_paint.origin_box.y + (int) bg->m_position.y.calc_percent(bg_paint.origin_box.height - bg_paint.image_size.height); - } - - } - bg_paint.border_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height); - bg_paint.border_box = border_box; - bg_paint.is_root = have_parent() ? false : true; -} - -litehtml::visibility litehtml::html_tag::get_visibility() const -{ - return m_visibility; -} - -void litehtml::html_tag::draw_list_marker( uint_ptr hdc, const position &pos ) -{ - list_marker lm; - - const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0); - size img_size; - if(list_image) - { - css::parse_css_url(list_image, lm.image); - lm.baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0); - get_document()->container()->get_image_size(lm.image.c_str(), lm.baseurl, img_size); - } else - { - lm.baseurl = 0; - } - - int ln_height = line_height(); - int sz_font = get_font_size(); - lm.pos.x = pos.x; - lm.pos.width = sz_font - sz_font * 2 / 3; - lm.color = get_color(_t("color"), true, web_color(0, 0, 0)); - lm.marker_type = m_list_style_type; - lm.font = get_font(); - - if (m_list_style_type >= list_style_type_armenian) - { - lm.pos.y = pos.y; - lm.pos.height = pos.height; - lm.index = get_attr(_t("list_index"), _t(""))[0]; - } - else - { - lm.pos.height = sz_font - sz_font * 2 / 3; - lm.pos.y = pos.y + ln_height / 2 - lm.pos.height / 2; - lm.index = -1; - } - - if(img_size.width && img_size.height) - { - if(lm.pos.y + img_size.height > pos.y + pos.height) - { - lm.pos.y = pos.y + pos.height - img_size.height; - } - if(img_size.width > lm.pos.width) - { - lm.pos.x -= img_size.width - lm.pos.width; - } - - lm.pos.width = img_size.width; - lm.pos.height = img_size.height; - } - - if (m_list_style_position == list_style_position_outside) - { - if (m_list_style_type >= list_style_type_armenian) - { - auto tw_space = get_document()->container()->text_width(_t(" "), lm.font); - lm.pos.x = pos.x - tw_space * 2; - lm.pos.width = tw_space; - } - else - { - lm.pos.x -= sz_font; - } - } - - if (m_list_style_type >= list_style_type_armenian) - { - auto marker_text = get_list_marker_text(lm.index); - lm.pos.height = ln_height; - if (marker_text.empty()) - { - get_document()->container()->draw_list_marker(hdc, lm); - } - else - { - marker_text += _t("."); - auto tw = get_document()->container()->text_width(marker_text.c_str(), lm.font); - auto text_pos = lm.pos; - text_pos.move_to(text_pos.right() - tw, text_pos.y); - text_pos.width = tw; - get_document()->container()->draw_text(hdc, marker_text.c_str(), lm.font, lm.color, text_pos); - } - } - else - { - get_document()->container()->draw_list_marker(hdc, lm); - } -} - -litehtml::tstring litehtml::html_tag::get_list_marker_text(int index) -{ - switch (m_list_style_type) - { - case litehtml::list_style_type_decimal: - return t_to_string(index); - case litehtml::list_style_type_decimal_leading_zero: - { - auto txt = t_to_string(index); - if (txt.length() == 1) - { - txt = _t("0") + txt; - } - return txt; - } - case litehtml::list_style_type_lower_latin: - case litehtml::list_style_type_lower_alpha: - return num_cvt::to_latin_lower(index); - case litehtml::list_style_type_lower_greek: - return num_cvt::to_greek_lower(index); - case litehtml::list_style_type_upper_alpha: - case litehtml::list_style_type_upper_latin: - return num_cvt::to_latin_upper(index); - case litehtml::list_style_type_lower_roman: - return num_cvt::to_roman_lower(index); - case litehtml::list_style_type_upper_roman: - return num_cvt::to_roman_upper(index); - case litehtml::list_style_type_armenian: - break; - case litehtml::list_style_type_georgian: - break; - case litehtml::list_style_type_hebrew: - break; - case litehtml::list_style_type_hiragana: - break; - case litehtml::list_style_type_hiragana_iroha: - break; - case litehtml::list_style_type_katakana: - break; - case litehtml::list_style_type_katakana_iroha: - break; - default: - break; - } - return _t(""); -} - -void litehtml::html_tag::draw_children( uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex ) -{ - if (m_display == display_table || m_display == display_inline_table) - { - draw_children_table(hdc, x, y, clip, flag, zindex); - } - else - { - draw_children_box(hdc, x, y, clip, flag, zindex); - } -} - -bool litehtml::html_tag::fetch_positioned() -{ - bool ret = false; - - m_positioned.clear(); - - litehtml::element_position el_pos; - - for(auto& el : m_children) - { - el_pos = el->get_element_position(); - if (el_pos != element_position_static) - { - add_positioned(el); - } - if (!ret && (el_pos == element_position_absolute || el_pos == element_position_fixed)) - { - ret = true; - } - if(el->fetch_positioned()) - { - ret = true; - } - } - return ret; -} - -int litehtml::html_tag::get_zindex() const -{ - return m_z_index; -} - -void litehtml::html_tag::render_positioned(render_type rt) -{ - position wnd_position; - get_document()->container()->get_client_rect(wnd_position); - - element_position el_position; - bool process; - for (auto& el : m_positioned) - { - el_position = el->get_element_position(); - - process = false; - if(el->get_display() != display_none) - { - if(el_position == element_position_absolute) - { - if(rt != render_fixed_only) - { - process = true; - } - } else if(el_position == element_position_fixed) - { - if(rt != render_no_fixed) - { - process = true; - } - } - } - - if(process) - { - int parent_height = 0; - int parent_width = 0; - if(el_position == element_position_fixed) - { - parent_height = wnd_position.height; - parent_width = wnd_position.width; - } else - { - element::ptr el_parent = el->parent(); - if(el_parent) - { - parent_height = el_parent->height(); - parent_width = el_parent->width(); - } - } - - css_length css_left = el->get_css_left(); - css_length css_right = el->get_css_right(); - css_length css_top = el->get_css_top(); - css_length css_bottom = el->get_css_bottom(); - - bool need_render = false; - - css_length el_w = el->get_css_width(); - css_length el_h = el->get_css_height(); - - int new_width = -1; - int new_height = -1; - if(el_w.units() == css_units_percentage && parent_width) - { - new_width = el_w.calc_percent(parent_width); - if(el->m_pos.width != new_width) - { - need_render = true; - el->m_pos.width = new_width; - } - } - - if(el_h.units() == css_units_percentage && parent_height) - { - new_height = el_h.calc_percent(parent_height); - if(el->m_pos.height != new_height) - { - need_render = true; - el->m_pos.height = new_height; - } - } - - bool cvt_x = false; - bool cvt_y = false; - - if(el_position == element_position_fixed) - { - if(!css_left.is_predefined() || !css_right.is_predefined()) - { - if(!css_left.is_predefined() && css_right.is_predefined()) - { - el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left(); - } else if(css_left.is_predefined() && !css_right.is_predefined()) - { - el->m_pos.x = parent_width - css_right.calc_percent(parent_width) - el->m_pos.width - el->content_margins_right(); - } else - { - el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left(); - el->m_pos.width = parent_width - css_left.calc_percent(parent_width) - css_right.calc_percent(parent_width) - (el->content_margins_left() + el->content_margins_right()); - need_render = true; - } - } - - if(!css_top.is_predefined() || !css_bottom.is_predefined()) - { - if(!css_top.is_predefined() && css_bottom.is_predefined()) - { - el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top(); - } else if(css_top.is_predefined() && !css_bottom.is_predefined()) - { - el->m_pos.y = parent_height - css_bottom.calc_percent(parent_height) - el->m_pos.height - el->content_margins_bottom(); - } else - { - el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top(); - el->m_pos.height = parent_height - css_top.calc_percent(parent_height) - css_bottom.calc_percent(parent_height) - (el->content_margins_top() + el->content_margins_bottom()); - need_render = true; - } - } - } else - { - if(!css_left.is_predefined() || !css_right.is_predefined()) - { - if(!css_left.is_predefined() && css_right.is_predefined()) - { - el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left() - m_padding.left; - } else if(css_left.is_predefined() && !css_right.is_predefined()) - { - el->m_pos.x = m_pos.width + m_padding.right - css_right.calc_percent(parent_width) - el->m_pos.width - el->content_margins_right(); - } else - { - el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left() - m_padding.left; - el->m_pos.width = m_pos.width + m_padding.left + m_padding.right - css_left.calc_percent(parent_width) - css_right.calc_percent(parent_width) - (el->content_margins_left() + el->content_margins_right()); - if (new_width != -1) - { - el->m_pos.x += (el->m_pos.width - new_width) / 2; - el->m_pos.width = new_width; - } - need_render = true; - } - cvt_x = true; - } - - if(!css_top.is_predefined() || !css_bottom.is_predefined()) - { - if(!css_top.is_predefined() && css_bottom.is_predefined()) - { - el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top() - m_padding.top; - } else if(css_top.is_predefined() && !css_bottom.is_predefined()) - { - el->m_pos.y = m_pos.height + m_padding.bottom - css_bottom.calc_percent(parent_height) - el->m_pos.height - el->content_margins_bottom(); - } else - { - el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top() - m_padding.top; - el->m_pos.height = m_pos.height + m_padding.top + m_padding.bottom - css_top.calc_percent(parent_height) - css_bottom.calc_percent(parent_height) - (el->content_margins_top() + el->content_margins_bottom()); - if (new_height != -1) - { - el->m_pos.y += (el->m_pos.height - new_height) / 2; - el->m_pos.height = new_height; - } - need_render = true; - } - cvt_y = true; - } - } - - if(cvt_x || cvt_y) - { - int offset_x = 0; - int offset_y = 0; - element::ptr cur_el = el->parent(); - element::ptr this_el = shared_from_this(); - while(cur_el && cur_el != this_el) - { - offset_x += cur_el->m_pos.x; - offset_y += cur_el->m_pos.y; - cur_el = cur_el->parent(); - } - if(cvt_x) el->m_pos.x -= offset_x; - if(cvt_y) el->m_pos.y -= offset_y; - } - - if(need_render) - { - position pos = el->m_pos; - el->render(el->left(), el->top(), el->width(), true); - el->m_pos = pos; - } - - if(el_position == element_position_fixed) - { - position fixed_pos; - el->get_redraw_box(fixed_pos); - get_document()->add_fixed_box(fixed_pos); - } - } - - el->render_positioned(); - } - - if(!m_positioned.empty()) - { - std::stable_sort(m_positioned.begin(), m_positioned.end(), [](const litehtml::element::ptr& _Left, const litehtml::element::ptr& _Right) - { - return (_Left->get_zindex() < _Right->get_zindex()); - }); - } -} - -void litehtml::html_tag::draw_stacking_context( uint_ptr hdc, int x, int y, const position* clip, bool with_positioned ) -{ - if(!is_visible()) return; - - std::map<int, bool> zindexes; - if(with_positioned) - { - for(elements_vector::iterator i = m_positioned.begin(); i != m_positioned.end(); i++) - { - zindexes[(*i)->get_zindex()]; - } - - for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++) - { - if(idx->first < 0) - { - draw_children(hdc, x, y, clip, draw_positioned, idx->first); - } - } - } - draw_children(hdc, x, y, clip, draw_block, 0); - draw_children(hdc, x, y, clip, draw_floats, 0); - draw_children(hdc, x, y, clip, draw_inlines, 0); - if(with_positioned) - { - for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++) - { - if(idx->first == 0) - { - draw_children(hdc, x, y, clip, draw_positioned, idx->first); - } - } - - for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++) - { - if(idx->first > 0) - { - draw_children(hdc, x, y, clip, draw_positioned, idx->first); - } - } - } -} - -litehtml::overflow litehtml::html_tag::get_overflow() const -{ - return m_overflow; -} - -bool litehtml::html_tag::is_nth_child(const element::ptr& el, int num, int off, bool of_type) const -{ - int idx = 1; - for(const auto& child : m_children) - { - if(child->get_display() != display_inline_text) - { - if( (!of_type) || (of_type && !t_strcmp(el->get_tagName(), child->get_tagName())) ) - { - if(el == child) - { - if(num != 0) - { - if((idx - off) >= 0 && (idx - off) % num == 0) - { - return true; - } - - } else if(idx == off) - { - return true; - } - return false; - } - idx++; - } - if(el == child) break; - } - } - return false; -} - -bool litehtml::html_tag::is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const -{ - int idx = 1; - for(elements_vector::const_reverse_iterator child = m_children.rbegin(); child != m_children.rend(); child++) - { - if((*child)->get_display() != display_inline_text) - { - if( !of_type || (of_type && !t_strcmp(el->get_tagName(), (*child)->get_tagName())) ) - { - if(el == (*child)) - { - if(num != 0) - { - if((idx - off) >= 0 && (idx - off) % num == 0) - { - return true; - } - - } else if(idx == off) - { - return true; - } - return false; - } - idx++; - } - if(el == (*child)) break; - } - } - return false; -} - -void litehtml::html_tag::parse_nth_child_params( tstring param, int &num, int &off ) -{ - if(param == _t("odd")) - { - num = 2; - off = 1; - } else if(param == _t("even")) - { - num = 2; - off = 0; - } else - { - string_vector tokens; - split_string(param, tokens, _t(" n"), _t("n")); - - tstring s_num; - tstring s_off; - - tstring s_int; - for(string_vector::iterator tok = tokens.begin(); tok != tokens.end(); tok++) - { - if((*tok) == _t("n")) - { - s_num = s_int; - s_int.clear(); - } else - { - s_int += (*tok); - } - } - s_off = s_int; - - num = t_atoi(s_num.c_str()); - off = t_atoi(s_off.c_str()); - } -} - -void litehtml::html_tag::calc_document_size( litehtml::size& sz, int x /*= 0*/, int y /*= 0*/ ) -{ - if(is_visible() && m_el_position != element_position_fixed) - { - element::calc_document_size(sz, x, y); - - if(m_overflow == overflow_visible) - { - for(auto& el : m_children) - { - el->calc_document_size(sz, x + m_pos.x, y + m_pos.y); - } - } - - // root element (<html>) must to cover entire window - if(!have_parent()) - { - position client_pos; - get_document()->container()->get_client_rect(client_pos); - m_pos.height = std::max(sz.height, client_pos.height) - content_margins_top() - content_margins_bottom(); - m_pos.width = std::max(sz.width, client_pos.width) - content_margins_left() - content_margins_right(); - } - } -} - - -void litehtml::html_tag::get_redraw_box(litehtml::position& pos, int x /*= 0*/, int y /*= 0*/) -{ - if(is_visible()) - { - element::get_redraw_box(pos, x, y); - - if(m_overflow == overflow_visible) - { - for(auto& el : m_children) - { - if(el->get_element_position() != element_position_fixed) - { - el->get_redraw_box(pos, x + m_pos.x, y + m_pos.y); - } - } - } - } -} - -litehtml::element::ptr litehtml::html_tag::find_adjacent_sibling( const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/ ) -{ - element::ptr ret; - for(auto& e : m_children) - { - if(e->get_display() != display_inline_text) - { - if(e == el) - { - if(ret) - { - int res = ret->select(selector, apply_pseudo); - if(res != select_no_match) - { - if(is_pseudo) - { - if(res & select_match_pseudo_class) - { - *is_pseudo = true; - } else - { - *is_pseudo = false; - } - } - return ret; - } - } - return 0; - } else - { - ret = e; - } - } - } - return 0; -} - -litehtml::element::ptr litehtml::html_tag::find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/) -{ - element::ptr ret = 0; - for(auto& e : m_children) - { - if(e->get_display() != display_inline_text) - { - if(e == el) - { - return ret; - } else if(!ret) - { - int res = e->select(selector, apply_pseudo); - if(res != select_no_match) - { - if(is_pseudo) - { - if(res & select_match_pseudo_class) - { - *is_pseudo = true; - } else - { - *is_pseudo = false; - } - } - ret = e; - } - } - } - } - return 0; -} - -bool litehtml::html_tag::is_only_child(const element::ptr& el, bool of_type) const -{ - int child_count = 0; - for(const auto& child : m_children) - { - if(child->get_display() != display_inline_text) - { - if( !of_type || (of_type && !t_strcmp(el->get_tagName(), child->get_tagName())) ) - { - child_count++; - } - if(child_count > 1) break; - } - } - if(child_count > 1) - { - return false; - } - return true; + return false; } -void litehtml::html_tag::update_floats(int dy, const element::ptr &parent) +void litehtml::html_tag::set_data( const char* /*data*/ ) { - if(is_floats_holder()) - { - bool reset_cache = false; - for(floated_box::vector::reverse_iterator fb = m_floats_left.rbegin(); fb != m_floats_left.rend(); fb++) - { - if(fb->el->is_ancestor(parent)) - { - reset_cache = true; - fb->pos.y += dy; - } - } - if(reset_cache) - { - m_cahe_line_left.invalidate(); - } - reset_cache = false; - for(floated_box::vector::reverse_iterator fb = m_floats_right.rbegin(); fb != m_floats_right.rend(); fb++) - { - if(fb->el->is_ancestor(parent)) - { - reset_cache = true; - fb->pos.y += dy; - } - } - if(reset_cache) - { - m_cahe_line_right.invalidate(); - } - } else + +} + +bool litehtml::html_tag::on_mouse_over() +{ + bool ret = false; + + element::ptr el = shared_from_this(); + while(el) { - element::ptr el_parent = this->parent(); - if (el_parent) + if(el->set_pseudo_class(_hover_, true)) { - el_parent->update_floats(dy, parent); + ret = true; } + el = el->parent(); } + + return ret; } -void litehtml::html_tag::remove_before_after() +bool litehtml::html_tag::on_mouse_leave() { - if(!m_children.empty()) + bool ret = false; + + element::ptr el = shared_from_this(); + while(el) { - if( !t_strcmp(m_children.front()->get_tagName(), _t("::before")) ) + if(el->set_pseudo_class(_hover_, false)) { - m_children.erase(m_children.begin()); + ret = true; } - } - if(!m_children.empty()) - { - if( !t_strcmp(m_children.back()->get_tagName(), _t("::after")) ) + if(el->set_pseudo_class(_active_, false)) { - m_children.erase(m_children.end() - 1); + ret = true; } + el = el->parent(); } + + return ret; } -litehtml::element::ptr litehtml::html_tag::get_element_before() +bool litehtml::html_tag::on_lbutton_down() { - if(!m_children.empty()) + bool ret = false; + + element::ptr el = shared_from_this(); + while (el) { - if( !t_strcmp(m_children.front()->get_tagName(), _t("::before")) ) + if (el->set_pseudo_class(_active_, true)) { - return m_children.front(); + ret = true; } + el = el->parent(); } - element::ptr el = std::make_shared<el_before>(get_document()); - el->parent(shared_from_this()); - m_children.insert(m_children.begin(), el); - return el; + + return ret; } -litehtml::element::ptr litehtml::html_tag::get_element_after() +bool litehtml::html_tag::on_lbutton_up() { - if(!m_children.empty()) + bool ret = false; + + element::ptr el = shared_from_this(); + while (el) { - if( !t_strcmp(m_children.back()->get_tagName(), _t("::after")) ) + if (el->set_pseudo_class(_active_, false)) { - return m_children.back(); + ret = true; } + el = el->parent(); } - element::ptr el = std::make_shared<el_after>(get_document()); - appendChild(el); - return el; -} -void litehtml::html_tag::add_style( const litehtml::style& st ) -{ - m_style.combine(st); + on_click(); + + return ret; } -bool litehtml::html_tag::have_inline_child() const +void litehtml::html_tag::on_click() { - if(!m_children.empty()) + if (!is_root()) { - for(const auto& el : m_children) + element::ptr el_parent = parent(); + if (el_parent) { - if(!el->is_white_space()) - { - return true; - } + el_parent->on_click(); } } - return false; } -void litehtml::html_tag::refresh_styles() +bool litehtml::html_tag::is_break() const { - remove_before_after(); + return false; +} - for (auto& el : m_children) - { - if(el->get_display() != display_inline_text) - { - el->refresh_styles(); - } - } +void litehtml::html_tag::draw_background(uint_ptr hdc, int x, int y, const position *clip, + const std::shared_ptr<render_item> &ri) +{ + position pos = ri->pos(); + pos.x += x; + pos.y += y; - m_style.clear(); + position el_pos = pos; + el_pos += ri->get_paddings(); + el_pos += ri->get_margins(); - for (auto& usel : m_used_styles) + if(m_css.get_display() != display_inline && m_css.get_display() != display_table_row) { - usel->m_used = false; - - if(usel->m_selector->is_media_valid()) + if(el_pos.does_intersect(clip) || is_root()) { - int apply = select(*usel->m_selector, false); + auto v_offset = ri->get_draw_vertical_offset(); + pos.y += v_offset; + pos.height -= v_offset; - if(apply != select_no_match) + const background* bg = get_background(); + if(bg) { - if(apply & select_match_pseudo_class) - { - if(select(*usel->m_selector, true)) - { - if(apply & select_match_with_after) - { - element::ptr el = get_element_after(); - if(el) - { - el->add_style(*usel->m_selector->m_style); - } - } else if(apply & select_match_with_before) - { - element::ptr el = get_element_before(); - if(el) - { - el->add_style(*usel->m_selector->m_style); - } - } - else - { - add_style(*usel->m_selector->m_style); - usel->m_used = true; - } - } - } else if(apply & select_match_with_after) + std::vector<background_paint> bg_paint; + init_background_paint(pos, bg_paint, bg, ri); + if(is_root()) { - element::ptr el = get_element_after(); - if(el) - { - el->add_style(*usel->m_selector->m_style); - } - } else if(apply & select_match_with_before) - { - element::ptr el = get_element_before(); - if(el) + for(auto& b : bg_paint) { - el->add_style(*usel->m_selector->m_style); + b.clip_box = *clip; + b.border_box = *clip; } - } else - { - add_style(*usel->m_selector->m_style); - usel->m_used = true; } - } - } - } -} -litehtml::element::ptr litehtml::html_tag::get_child_by_point(int x, int y, int client_x, int client_y, draw_flag flag, int zindex) -{ - element::ptr ret = 0; + get_document()->container()->draw_background(hdc, bg_paint); + } + position border_box = pos; + border_box += ri->get_paddings(); + border_box += ri->get_borders(); - if(m_overflow > overflow_visible) - { - if(!m_pos.is_point_inside(x, y)) - { - return ret; + borders bdr = m_css.get_borders(); + if(bdr.is_visible()) + { + bdr.radius = m_css.get_borders().radius.calc_percents(border_box.width, border_box.height); + get_document()->container()->draw_borders(hdc, bdr, border_box, is_root()); + } } - } + } else + { + const background* bg = get_background(); - position pos = m_pos; - pos.x = x - pos.x; - pos.y = y - pos.y; + position::vector boxes; + ri->get_inline_boxes(boxes); - for(elements_vector::reverse_iterator i = m_children.rbegin(); i != m_children.rend() && !ret; i++) - { - element::ptr el = (*i); + std::vector<background_paint> bg_paint; + position content_box; - if(el->is_visible() && el->get_display() != display_inline_text) + for(auto box = boxes.begin(); box != boxes.end(); box++) { - switch(flag) + box->x += x; + box->y += y; + + if(box->does_intersect(clip)) { - case draw_positioned: - if(el->is_positioned() && el->get_zindex() == zindex) + content_box = *box; + content_box -= ri->get_borders(); + content_box -= ri->get_paddings(); + + if(bg) { - if(el->get_element_position() == element_position_fixed) - { - ret = el->get_element_by_point(client_x, client_y, client_x, client_y); - if(!ret && (*i)->is_point_inside(client_x, client_y)) - { - ret = (*i); - } - } else - { - ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y); - if(!ret && (*i)->is_point_inside(pos.x, pos.y)) - { - ret = (*i); - } - } - el = 0; + init_background_paint(content_box, bg_paint, bg, ri); } - break; - case draw_block: - if(!el->is_inline_box() && el->get_float() == float_none && !el->is_positioned()) + + css_borders bdr; + + // set left borders radius for the first box + if(box == boxes.begin()) { - if(el->is_point_inside(pos.x, pos.y)) - { - ret = el; - } + bdr.radius.bottom_left_x = m_css.get_borders().radius.bottom_left_x; + bdr.radius.bottom_left_y = m_css.get_borders().radius.bottom_left_y; + bdr.radius.top_left_x = m_css.get_borders().radius.top_left_x; + bdr.radius.top_left_y = m_css.get_borders().radius.top_left_y; } - break; - case draw_floats: - if(el->get_float() != float_none && !el->is_positioned()) + + // set right borders radius for the last box + if(box == boxes.end() - 1) { - ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y); + bdr.radius.bottom_right_x = m_css.get_borders().radius.bottom_right_x; + bdr.radius.bottom_right_y = m_css.get_borders().radius.bottom_right_y; + bdr.radius.top_right_x = m_css.get_borders().radius.top_right_x; + bdr.radius.top_right_y = m_css.get_borders().radius.top_right_y; + } - if(!ret && (*i)->is_point_inside(pos.x, pos.y)) - { - ret = (*i); - } - el = 0; + + bdr.top = m_css.get_borders().top; + bdr.bottom = m_css.get_borders().bottom; + if(box == boxes.begin()) + { + bdr.left = m_css.get_borders().left; } - break; - case draw_inlines: - if(el->is_inline_box() && el->get_float() == float_none && !el->is_positioned()) + if(box == boxes.end() - 1) { - if(el->get_display() == display_inline_block) - { - ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y); - el = 0; - } - if(!ret && (*i)->is_point_inside(pos.x, pos.y)) - { - ret = (*i); - } + bdr.right = m_css.get_borders().right; } - break; - default: - break; - } - if(el && !el->is_positioned()) - { - if(flag == draw_positioned) + if(bg) { - element::ptr child = el->get_child_by_point(pos.x, pos.y, client_x, client_y, flag, zindex); - if(child) + for (auto& bgp : bg_paint) { - ret = child; + bgp.border_radius = bdr.radius.calc_percents(bgp.border_box.width, bgp.border_box.width); } - } else + get_document()->container()->draw_background(hdc, bg_paint); + } + if(bdr.is_visible()) { - if( el->get_float() == float_none && - el->get_display() != display_inline_block) - { - element::ptr child = el->get_child_by_point(pos.x, pos.y, client_x, client_y, flag, zindex); - if(child) - { - ret = child; - } - } + borders b = bdr; + b.radius = bdr.radius.calc_percents(box->width, box->height); + get_document()->container()->draw_borders(hdc, b, *box, false); } } } } - - return ret; } -litehtml::element::ptr litehtml::html_tag::get_element_by_point(int x, int y, int client_x, int client_y) +bool litehtml::html_tag::set_pseudo_class( string_id cls, bool add ) { - if(!is_visible()) return 0; - - element::ptr ret; - - std::map<int, bool> zindexes; - - for(elements_vector::iterator i = m_positioned.begin(); i != m_positioned.end(); i++) - { - zindexes[(*i)->get_zindex()]; - } - - for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++) - { - if(idx->first > 0) - { - ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first); - } - } - if(ret) return ret; - - for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++) - { - if(idx->first == 0) - { - ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first); - } - } - if(ret) return ret; - - ret = get_child_by_point(x, y, client_x, client_y, draw_inlines, 0); - if(ret) return ret; - - ret = get_child_by_point(x, y, client_x, client_y, draw_floats, 0); - if(ret) return ret; - - ret = get_child_by_point(x, y, client_x, client_y, draw_block, 0); - if(ret) return ret; - - - for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++) - { - if(idx->first < 0) - { - ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first); - } - } - if(ret) return ret; - - if(m_el_position == element_position_fixed) + bool ret = false; + if(add) { - if(is_point_inside(client_x, client_y)) + if(std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), cls) == m_pseudo_classes.end()) { - ret = shared_from_this(); + m_pseudo_classes.push_back(cls); + ret = true; } } else { - if(is_point_inside(x, y)) + auto pi = std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), cls); + if(pi != m_pseudo_classes.end()) { - ret = shared_from_this(); + m_pseudo_classes.erase(pi); + ret = true; } } - return ret; } -const litehtml::background* litehtml::html_tag::get_background(bool own_only) +bool litehtml::html_tag::set_class( const char* pclass, bool add ) { - if(own_only) - { - // return own background with check for empty one - if(m_bg.m_image.empty() && !m_bg.m_color.alpha) - { - return 0; - } - return &m_bg; - } + string_vector classes; + bool changed = false; + + split_string( pclass, classes, " " ); - if(m_bg.m_image.empty() && !m_bg.m_color.alpha) + if(add) { - // if this is root element (<html>) try to get background from body - if (!have_parent()) + for( auto & _class : classes ) { - for (const auto& el : m_children) + if(std::find(m_str_classes.begin(), m_str_classes.end(), _class) == m_str_classes.end()) { - if( el->is_body() ) - { - // return own body background - return el->get_background(true); - } - } - } - return 0; - } - - if(is_body()) + m_str_classes.push_back( std::move( _class ) ); + changed = true; + } + } + } else { - element::ptr el_parent = parent(); - if (el_parent) + for( const auto & _class : classes ) { - if (!el_parent->get_background(true)) + auto end = std::remove(m_str_classes.begin(), m_str_classes.end(), _class); + + if(end != m_str_classes.end()) { - // parent of body will draw background for body - return 0; + m_str_classes.erase(end, m_str_classes.end()); + changed = true; } } } - return &m_bg; + if( changed ) + { + string class_string; + join_string(class_string, m_str_classes, " "); + set_attr("class", class_string.c_str()); + + return true; + } + else + { + return false; + } + } -int litehtml::html_tag::render_box(int x, int y, int max_width, bool second_pass /*= false*/) +bool litehtml::html_tag::is_replaced() const { - int parent_width = max_width; - - if (max_width <= 0) - return 0; + return false; +} - calc_outlines(parent_width); +bool litehtml::html_tag::is_floats_holder() const +{ + if( m_css.get_display() == display_inline_block || + m_css.get_display() == display_table_cell || + is_root() || + m_css.get_float() != float_none || + m_css.get_position() == element_position_absolute || + m_css.get_position() == element_position_fixed || + m_css.get_overflow() > overflow_visible) + { + return true; + } + return false; +} - m_pos.clear(); - m_pos.move_to(x, y); +size_t litehtml::html_tag::get_children_count() const +{ + return m_children.size(); +} - m_pos.x += content_margins_left(); - m_pos.y += content_margins_top(); +litehtml::element::ptr litehtml::html_tag::get_child( int idx ) const +{ + return m_children[idx]; +} - int ret_width = 0; - def_value<int> block_width(0); +void litehtml::html_tag::init_background_paint(position pos, std::vector<background_paint>& bg_paint, const background* bg, const std::shared_ptr<render_item>& ri) +{ + bg_paint = { background_paint() }; + if (!bg) return; - if (m_display != display_table_cell && !m_css_width.is_predefined()) - { - int w = calc_width(parent_width); - - if (m_box_sizing == box_sizing_border_box) - { - w -= m_padding.width() + m_borders.width(); - } - ret_width = max_width = block_width = w; - } - else - { - if (max_width) - { - max_width -= content_margins_left() + content_margins_right(); - } - } + int bg_count = std::max((int)bg->m_image.size(), 1); + bg_paint.resize(bg_count); - // check for max-width (on the first pass only) - if (!m_css_max_width.is_predefined() && !second_pass) + for (int i = 0; i < bg_count; i++) { - int mw = get_document()->cvt_units(m_css_max_width, m_font_size, parent_width); - if (m_box_sizing == box_sizing_border_box) - { - mw -= m_padding.left + m_borders.left + m_padding.right + m_borders.right; - } - if (max_width > mw) - { - max_width = mw; - } + init_one_background_paint(i, pos, bg_paint[i], bg, ri); } - m_floats_left.clear(); - m_floats_right.clear(); - m_boxes.clear(); - m_cahe_line_left.invalidate(); - m_cahe_line_right.invalidate(); - - element_position el_position; + bg_paint.back().color = bg->m_color; +} - int block_height = 0; +void litehtml::html_tag::init_one_background_paint(int i, position pos, background_paint& bg_paint, const background* bg, const std::shared_ptr<render_item>& ri) +{ + bg_paint.image = i < bg->m_image.size() ? bg->m_image[i] : ""; + bg_paint.baseurl = bg->m_baseurl; + bg_paint.attachment = i < bg->m_attachment.size() ? (background_attachment)bg->m_attachment[i] : background_attachment_scroll; + bg_paint.repeat = i < bg->m_repeat.size() ? (background_repeat)bg->m_repeat[i] : background_repeat_repeat; + int clip = i < bg->m_clip.size() ? bg->m_clip[i] : background_box_border; + int origin = i < bg->m_origin.size() ? bg->m_origin[i] : background_box_padding; + const css_size auto_auto(css_length::predef_value(background_size_auto), css_length::predef_value(background_size_auto)); + css_size size = i < bg->m_size.size() ? bg->m_size[i] : auto_auto; + css_length position_x = i < bg->m_position_x.size() ? bg->m_position_x[i] : css_length(0, css_units_percentage); + css_length position_y = i < bg->m_position_y.size() ? bg->m_position_y[i] : css_length(0, css_units_percentage); - m_pos.height = 0; + position content_box = pos; + position padding_box = pos; + padding_box += ri->get_paddings(); + position border_box = padding_box; + border_box += ri->get_borders(); - if (get_predefined_height(block_height)) + switch(clip) { - m_pos.height = block_height; + case background_box_padding: + bg_paint.clip_box = padding_box; + break; + case background_box_content: + bg_paint.clip_box = content_box; + break; + default: + bg_paint.clip_box = border_box; + break; } - white_space ws = get_white_space(); - bool skip_spaces = false; - if (ws == white_space_normal || - ws == white_space_nowrap || - ws == white_space_pre_line) + switch(origin) { - skip_spaces = true; + case background_box_border: + bg_paint.origin_box = border_box; + break; + case background_box_content: + bg_paint.origin_box = content_box; + break; + default: + bg_paint.origin_box = padding_box; + break; } - bool was_space = false; - - for (auto el : m_children) + if(!bg_paint.image.empty()) { - // we don't need process absolute and fixed positioned element on the second pass - if (second_pass) + get_document()->container()->get_image_size(bg_paint.image.c_str(), bg_paint.baseurl.c_str(), bg_paint.image_size); + if(bg_paint.image_size.width && bg_paint.image_size.height) { - el_position = el->get_element_position(); - if ((el_position == element_position_absolute || el_position == element_position_fixed)) continue; - } + litehtml::size img_new_sz = bg_paint.image_size; + double img_ar_width = (double) bg_paint.image_size.width / (double) bg_paint.image_size.height; + double img_ar_height = (double) bg_paint.image_size.height / (double) bg_paint.image_size.width; - // skip spaces to make rendering a bit faster - if (skip_spaces) - { - if (el->is_white_space()) + + if(size.width.is_predefined()) { - if (was_space) + switch(size.width.predef()) { - el->skip(true); - continue; + case background_size_contain: + if( (int) ((double) bg_paint.origin_box.width * img_ar_height) <= bg_paint.origin_box.height ) + { + img_new_sz.width = bg_paint.origin_box.width; + img_new_sz.height = (int) ((double) bg_paint.origin_box.width * img_ar_height); + } else + { + img_new_sz.height = bg_paint.origin_box.height; + img_new_sz.width = (int) ((double) bg_paint.origin_box.height * img_ar_width); + } + break; + case background_size_cover: + if( (int) ((double) bg_paint.origin_box.width * img_ar_height) >= bg_paint.origin_box.height ) + { + img_new_sz.width = bg_paint.origin_box.width; + img_new_sz.height = (int) ((double) bg_paint.origin_box.width * img_ar_height); + } else + { + img_new_sz.height = bg_paint.origin_box.height; + img_new_sz.width = (int) ((double) bg_paint.origin_box.height * img_ar_width); + } + break; + break; + case background_size_auto: + if(!size.height.is_predefined()) + { + img_new_sz.height = size.height.calc_percent(bg_paint.origin_box.height); + img_new_sz.width = (int) ((double) img_new_sz.height * img_ar_width); + } + break; } - else + } else + { + img_new_sz.width = size.width.calc_percent(bg_paint.origin_box.width); + if(size.height.is_predefined()) + { + img_new_sz.height = (int) ((double) img_new_sz.width * img_ar_height); + } else { - was_space = true; + img_new_sz.height = size.height.calc_percent(bg_paint.origin_box.height); } } - else - { - was_space = false; - } - } - // place element into rendering flow - int rw = place_element(el, max_width); - if (rw > ret_width) - { - ret_width = rw; + bg_paint.image_size = img_new_sz; + bg_paint.position_x = bg_paint.origin_box.x + (int) position_x.calc_percent(bg_paint.origin_box.width - bg_paint.image_size.width); + bg_paint.position_y = bg_paint.origin_box.y + (int) position_y.calc_percent(bg_paint.origin_box.height - bg_paint.image_size.height); } } + bg_paint.border_radius = m_css.get_borders().radius.calc_percents(border_box.width, border_box.height); + bg_paint.border_box = border_box; + bg_paint.is_root = is_root(); +} - finish_last_box(true); +void litehtml::html_tag::draw_list_marker( uint_ptr hdc, const position& pos ) +{ + list_marker lm; - if (block_width.is_default() && is_inline_box()) + size img_size; + if (css().get_list_style_image() != "") { - m_pos.width = ret_width; - } - else + lm.image = css().get_list_style_image(); + lm.baseurl = css().get_list_style_image_baseurl().c_str(); + get_document()->container()->get_image_size(lm.image.c_str(), lm.baseurl, img_size); + } else { - m_pos.width = max_width; + lm.baseurl = nullptr; } - calc_auto_margins(parent_width); - if (!m_boxes.empty()) - { - if (collapse_top_margin()) - { - int old_top = m_margins.top; - m_margins.top = std::max(m_boxes.front()->top_margin(), m_margins.top); - if (m_margins.top != old_top) - { - update_floats(m_margins.top - old_top, shared_from_this()); - } - } - if (collapse_bottom_margin()) - { - m_margins.bottom = std::max(m_boxes.back()->bottom_margin(), m_margins.bottom); - m_pos.height = m_boxes.back()->bottom() - m_boxes.back()->bottom_margin(); - } - else - { - m_pos.height = m_boxes.back()->bottom(); - } - } + int ln_height = css().get_line_height(); + int sz_font = css().get_font_size(); + lm.pos.x = pos.x; + lm.pos.width = sz_font - sz_font * 2 / 3; + lm.color = css().get_color(); + lm.marker_type = css().get_list_style_type(); + lm.font = css().get_font(); - // add the floats height to the block height - if (is_floats_holder()) + if (css().get_list_style_type() >= list_style_type_armenian) { - int floats_height = get_floats_height(); - if (floats_height > m_pos.height) - { - m_pos.height = floats_height; - } + lm.pos.y = pos.y; + lm.pos.height = pos.height; + lm.index = atoi(get_attr("list_index", "0")); } - - // calculate the final position - - m_pos.move_to(x, y); - m_pos.x += content_margins_left(); - m_pos.y += content_margins_top(); - - if (get_predefined_height(block_height)) + else { - m_pos.height = block_height; + lm.pos.height = sz_font - sz_font * 2 / 3; + lm.pos.y = pos.y + ln_height / 2 - lm.pos.height / 2; + lm.index = -1; } - int min_height = 0; - if (!m_css_min_height.is_predefined() && m_css_min_height.units() == css_units_percentage) + if(img_size.width && img_size.height) { - element::ptr el_parent = parent(); - if (el_parent) + if(lm.pos.y + img_size.height > pos.y + pos.height) { - if (el_parent->get_predefined_height(block_height)) - { - min_height = m_css_min_height.calc_percent(block_height); - } + lm.pos.y = pos.y + pos.height - img_size.height; } - } - else - { - min_height = (int)m_css_min_height.val(); - } - if (min_height != 0 && m_box_sizing == box_sizing_border_box) - { - min_height -= m_padding.top + m_borders.top + m_padding.bottom + m_borders.bottom; - if (min_height < 0) min_height = 0; - } - - if (m_display == display_list_item) - { - const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0); - if (list_image) + if(img_size.width > lm.pos.width) { - tstring url; - css::parse_css_url(list_image, url); - - size sz; - const tchar_t* list_image_baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0); - get_document()->container()->get_image_size(url.c_str(), list_image_baseurl, sz); - if (min_height < sz.height) - { - min_height = sz.height; - } + lm.pos.x -= img_size.width - lm.pos.width; } + lm.pos.width = img_size.width; + lm.pos.height = img_size.height; } - if (min_height > m_pos.height) - { - m_pos.height = min_height; - } - - int min_width = m_css_min_width.calc_percent(parent_width); - - if (min_width != 0 && m_box_sizing == box_sizing_border_box) - { - min_width -= m_padding.left + m_borders.left + m_padding.right + m_borders.right; - if (min_width < 0) min_width = 0; - } - - if (min_width != 0) + if (m_css.get_list_style_position() == list_style_position_outside) { - if (min_width > m_pos.width) + if (m_css.get_list_style_type() >= list_style_type_armenian) { - m_pos.width = min_width; + if(lm.font) + { + auto tw_space = get_document()->container()->text_width(" ", lm.font); + lm.pos.x = pos.x - tw_space * 2; + lm.pos.width = tw_space; + } else + { + lm.pos.width = 0; + } } - if (min_width > ret_width) + else { - ret_width = min_width; + lm.pos.x -= sz_font; } } - ret_width += content_margins_left() + content_margins_right(); - - // re-render with new width - if (ret_width < max_width && !second_pass && have_parent()) + if (m_css.get_list_style_type() >= list_style_type_armenian) { - if (m_display == display_inline_block || - (m_css_width.is_predefined() && - (m_float != float_none || - m_display == display_table || - m_el_position == element_position_absolute || - m_el_position == element_position_fixed - )) - ) + auto marker_text = get_list_marker_text(lm.index); + lm.pos.height = ln_height; + if (marker_text.empty()) { - render(x, y, ret_width, true); - m_pos.width = ret_width - (content_margins_left() + content_margins_right()); + get_document()->container()->draw_list_marker(hdc, lm); } - } - - if (is_floats_holder() && !second_pass) - { - for (const auto& fb : m_floats_left) + else { - if (fb.el) + if(lm.font) { - fb.el->apply_relative_shift(fb.el->parent()->calc_width(m_pos.width)); + marker_text += "."; + auto tw = get_document()->container()->text_width(marker_text.c_str(), lm.font); + auto text_pos = lm.pos; + text_pos.move_to(text_pos.right() - tw, text_pos.y); + text_pos.width = tw; + get_document()->container()->draw_text(hdc, marker_text.c_str(), lm.font, lm.color, text_pos); } } } - - - return ret_width; -} - -int litehtml::html_tag::render_table(int x, int y, int max_width, bool second_pass /*= false*/) -{ - if (!m_grid) return 0; - - int parent_width = max_width; - - calc_outlines(parent_width); - - m_pos.clear(); - m_pos.move_to(x, y); - - m_pos.x += content_margins_left(); - m_pos.y += content_margins_top(); - - def_value<int> block_width(0); - - if (!m_css_width.is_predefined()) - { - max_width = block_width = calc_width(parent_width) - m_padding.width() - m_borders.width(); - } - else - { - if (max_width) - { - max_width -= content_margins_left() + content_margins_right(); - } - } - - // Calculate table spacing - int table_width_spacing = 0; - if (m_border_collapse == border_collapse_separate) - { - table_width_spacing = m_border_spacing_x * (m_grid->cols_count() + 1); - } else { - table_width_spacing = 0; - - if (m_grid->cols_count()) - { - table_width_spacing -= std::min(border_left(), m_grid->column(0).border_left); - table_width_spacing -= std::min(border_right(), m_grid->column(m_grid->cols_count() - 1).border_right); - } - - for (int col = 1; col < m_grid->cols_count(); col++) - { - table_width_spacing -= std::min(m_grid->column(col).border_left, m_grid->column(col - 1).border_right); - } + get_document()->container()->draw_list_marker(hdc, lm); } +} - - // Calculate the minimum content width (MCW) of each cell: the formatted content may span any number of lines but may not overflow the cell box. - // If the specified 'width' (W) of the cell is greater than MCW, W is the minimum cell width. A value of 'auto' means that MCW is the minimum - // cell width. - // - // Also, calculate the "maximum" cell width of each cell: formatting the content without breaking lines other than where explicit line breaks occur. - - if (m_grid->cols_count() == 1 && !block_width.is_default()) +litehtml::string litehtml::html_tag::get_list_marker_text(int index) +{ + switch (m_css.get_list_style_type()) { - for (int row = 0; row < m_grid->rows_count(); row++) + case litehtml::list_style_type_decimal: + return std::to_string(index); + case litehtml::list_style_type_decimal_leading_zero: { - table_cell* cell = m_grid->cell(0, row); - if (cell && cell->el) + auto txt = std::to_string(index); + if (txt.length() == 1) { - cell->min_width = cell->max_width = cell->el->render(0, 0, max_width - table_width_spacing); - cell->el->m_pos.width = cell->min_width - cell->el->content_margins_left() - cell->el->content_margins_right(); + txt = "0" + txt; } + return txt; } + case litehtml::list_style_type_lower_latin: + case litehtml::list_style_type_lower_alpha: + return num_cvt::to_latin_lower(index); + case litehtml::list_style_type_lower_greek: + return num_cvt::to_greek_lower(index); + case litehtml::list_style_type_upper_alpha: + case litehtml::list_style_type_upper_latin: + return num_cvt::to_latin_upper(index); + case litehtml::list_style_type_lower_roman: + return num_cvt::to_roman_lower(index); + case litehtml::list_style_type_upper_roman: + return num_cvt::to_roman_upper(index); + default: + return ""; +// case litehtml::list_style_type_armenian: +// case litehtml::list_style_type_georgian: +// case litehtml::list_style_type_hebrew: +// case litehtml::list_style_type_hiragana: +// case litehtml::list_style_type_hiragana_iroha: +// case litehtml::list_style_type_katakana: +// case litehtml::list_style_type_katakana_iroha: +// case litehtml::list_style_type_none: +// case litehtml::list_style_type_circle: +// case litehtml::list_style_type_disc: +// case litehtml::list_style_type_square: +// case litehtml::list_style_type_cjk_ideographic: +// break; } - else +} + +bool litehtml::html_tag::is_nth_child(const element::ptr& el, int num, int off, bool of_type) const +{ + int idx = 1; + for(const auto& child : m_children) { - for (int row = 0; row < m_grid->rows_count(); row++) + if(child->css().get_display() != display_inline_text) { - for (int col = 0; col < m_grid->cols_count(); col++) + if( (!of_type) || (of_type && el->tag() == child->tag()) ) { - table_cell* cell = m_grid->cell(col, row); - if (cell && cell->el) + if(el == child) { - if (!m_grid->column(col).css_width.is_predefined() && m_grid->column(col).css_width.units() != css_units_percentage) + if(num != 0) { - int css_w = m_grid->column(col).css_width.calc_percent(block_width); - int el_w = cell->el->render(0, 0, css_w); - cell->min_width = cell->max_width = std::max(css_w, el_w); - cell->el->m_pos.width = cell->min_width - cell->el->content_margins_left() - cell->el->content_margins_right(); - } - else + if((idx - off) >= 0 && (idx - off) % num == 0) + { + return true; + } + + } else if(idx == off) { - // calculate minimum content width - cell->min_width = cell->el->render(0, 0, 1); - // calculate maximum content width - cell->max_width = cell->el->render(0, 0, max_width - table_width_spacing); + return true; } + return false; } + idx++; } + if(el == child) break; } } + return false; +} - // For each column, determine a maximum and minimum column width from the cells that span only that column. - // The minimum is that required by the cell with the largest minimum cell width (or the column 'width', whichever is larger). - // The maximum is that required by the cell with the largest maximum cell width (or the column 'width', whichever is larger). - - for (int col = 0; col < m_grid->cols_count(); col++) - { - m_grid->column(col).max_width = 0; - m_grid->column(col).min_width = 0; - for (int row = 0; row < m_grid->rows_count(); row++) - { - if (m_grid->cell(col, row)->colspan <= 1) - { - m_grid->column(col).max_width = std::max(m_grid->column(col).max_width, m_grid->cell(col, row)->max_width); - m_grid->column(col).min_width = std::max(m_grid->column(col).min_width, m_grid->cell(col, row)->min_width); - } - } - } - - // For each cell that spans more than one column, increase the minimum widths of the columns it spans so that together, - // they are at least as wide as the cell. Do the same for the maximum widths. - // If possible, widen all spanned columns by approximately the same amount. - - for (int col = 0; col < m_grid->cols_count(); col++) +bool litehtml::html_tag::is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const +{ + int idx = 1; + for(auto child = m_children.rbegin(); child != m_children.rend(); child++) { - for (int row = 0; row < m_grid->rows_count(); row++) + if((*child)->css().get_display() != display_inline_text) { - if (m_grid->cell(col, row)->colspan > 1) + if( !of_type || (of_type && el->tag() == (*child)->tag()) ) { - int max_total_width = m_grid->column(col).max_width; - int min_total_width = m_grid->column(col).min_width; - for (int col2 = col + 1; col2 < col + m_grid->cell(col, row)->colspan; col2++) - { - max_total_width += m_grid->column(col2).max_width; - min_total_width += m_grid->column(col2).min_width; - } - if (min_total_width < m_grid->cell(col, row)->min_width) - { - m_grid->distribute_min_width(m_grid->cell(col, row)->min_width - min_total_width, col, col + m_grid->cell(col, row)->colspan - 1); - } - if (max_total_width < m_grid->cell(col, row)->max_width) + if(el == (*child)) { - m_grid->distribute_max_width(m_grid->cell(col, row)->max_width - max_total_width, col, col + m_grid->cell(col, row)->colspan - 1); + if(num != 0) + { + if((idx - off) >= 0 && (idx - off) % num == 0) + { + return true; + } + + } else if(idx == off) + { + return true; + } + return false; } + idx++; } + if(el == (*child)) break; } } + return false; +} - // If the 'table' or 'inline-table' element's 'width' property has a computed value (W) other than 'auto', the used width is the - // greater of W, CAPMIN, and the minimum width required by all the columns plus cell spacing or borders (MIN). - // If the used width is greater than MIN, the extra width should be distributed over the columns. - // - // If the 'table' or 'inline-table' element has 'width: auto', the used width is the greater of the table's containing block width, - // CAPMIN, and MIN. However, if either CAPMIN or the maximum width required by the columns plus cell spacing or borders (MAX) is - // less than that of the containing block, use max(MAX, CAPMIN). - - - int table_width = 0; - int min_table_width = 0; - int max_table_width = 0; - - if (!block_width.is_default()) - { - table_width = m_grid->calc_table_width(block_width - table_width_spacing, false, min_table_width, max_table_width); - } - else - { - table_width = m_grid->calc_table_width(max_width - table_width_spacing, true, min_table_width, max_table_width); - } - - min_table_width += table_width_spacing; - max_table_width += table_width_spacing; - table_width += table_width_spacing; - m_grid->calc_horizontal_positions(m_borders, m_border_collapse, m_border_spacing_x); - - bool row_span_found = false; - - // render cells with computed width - for (int row = 0; row < m_grid->rows_count(); row++) +litehtml::element::ptr litehtml::html_tag::find_adjacent_sibling( const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/ ) +{ + element::ptr ret; + for(auto& e : m_children) { - m_grid->row(row).height = 0; - for (int col = 0; col < m_grid->cols_count(); col++) + if(e->css().get_display() != display_inline_text) { - table_cell* cell = m_grid->cell(col, row); - if (cell->el) + if(e == el) { - int span_col = col + cell->colspan - 1; - if (span_col >= m_grid->cols_count()) - { - span_col = m_grid->cols_count() - 1; - } - int cell_width = m_grid->column(span_col).right - m_grid->column(col).left; - - if (cell->el->m_pos.width != cell_width - cell->el->content_margins_left() - cell->el->content_margins_right()) - { - cell->el->render(m_grid->column(col).left, 0, cell_width); - cell->el->m_pos.width = cell_width - cell->el->content_margins_left() - cell->el->content_margins_right(); - } - else - { - cell->el->m_pos.x = m_grid->column(col).left + cell->el->content_margins_left(); - } - - if (cell->rowspan <= 1) - { - m_grid->row(row).height = std::max(m_grid->row(row).height, cell->el->height()); - } - else + if(ret) { - row_span_found = true; + int res = ret->select(selector, apply_pseudo); + if(res != select_no_match) + { + if(is_pseudo) + { + if(res & select_match_pseudo_class) + { + *is_pseudo = true; + } else + { + *is_pseudo = false; + } + } + return ret; + } } - + return nullptr; + } else + { + ret = e; } } } + return nullptr; +} - if (row_span_found) +litehtml::element::ptr litehtml::html_tag::find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/) +{ + element::ptr ret = nullptr; + for(auto& e : m_children) { - for (int col = 0; col < m_grid->cols_count(); col++) + if(e->css().get_display() != display_inline_text) { - for (int row = 0; row < m_grid->rows_count(); row++) + if(e == el) + { + return ret; + } else if(!ret) { - table_cell* cell = m_grid->cell(col, row); - if (cell->el) + int res = e->select(selector, apply_pseudo); + if(res != select_no_match) { - int span_row = row + cell->rowspan - 1; - if (span_row >= m_grid->rows_count()) - { - span_row = m_grid->rows_count() - 1; - } - if (span_row != row) + if(is_pseudo) { - int h = 0; - for (int i = row; i <= span_row; i++) + if(res & select_match_pseudo_class) { - h += m_grid->row(i).height; - } - if (h < cell->el->height()) + *is_pseudo = true; + } else { - m_grid->row(span_row).height += cell->el->height() - h; + *is_pseudo = false; } } + ret = e; } } } } + return nullptr; +} - // Calculate vertical table spacing - int table_height_spacing = 0; - if (m_border_collapse == border_collapse_separate) - { - table_height_spacing = m_border_spacing_y * (m_grid->rows_count() + 1); - } - else - { - table_height_spacing = 0; - - if (m_grid->rows_count()) - { - table_height_spacing -= std::min(border_top(), m_grid->row(0).border_top); - table_height_spacing -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom); - } - - for (int row = 1; row < m_grid->rows_count(); row++) - { - table_height_spacing -= std::min(m_grid->row(row).border_top, m_grid->row(row - 1).border_bottom); - } - } - - - // calculate block height - int block_height = 0; - if (get_predefined_height(block_height)) - { - block_height -= m_padding.height() + m_borders.height(); - } - - // calculate minimum height from m_css_min_height - int min_height = 0; - if (!m_css_min_height.is_predefined() && m_css_min_height.units() == css_units_percentage) +bool litehtml::html_tag::is_only_child(const element::ptr& el, bool of_type) const +{ + int child_count = 0; + for(const auto& child : m_children) { - element::ptr el_parent = parent(); - if (el_parent) + if(child->css().get_display() != display_inline_text) { - int parent_height = 0; - if (el_parent->get_predefined_height(parent_height)) + if( !of_type || (of_type && el->tag() == child->tag()) ) { - min_height = m_css_min_height.calc_percent(parent_height); + child_count++; } + if(child_count > 1) break; } } - else + if(child_count > 1) { - min_height = (int)m_css_min_height.val(); + return false; } + return true; +} - int minimum_table_height = std::max(block_height, min_height); - - m_grid->calc_rows_height(minimum_table_height - table_height_spacing, m_border_spacing_y); - m_grid->calc_vertical_positions(m_borders, m_border_collapse, m_border_spacing_y); - - int table_height = 0; - - // place cells vertically - for (int col = 0; col < m_grid->cols_count(); col++) +litehtml::element::ptr litehtml::html_tag::get_element_before(const style& style, bool create) +{ + if(!m_children.empty()) { - for (int row = 0; row < m_grid->rows_count(); row++) + if( m_children.front()->tag() == __tag_before_ ) { - table_cell* cell = m_grid->cell(col, row); - if (cell->el) - { - int span_row = row + cell->rowspan - 1; - if (span_row >= m_grid->rows_count()) - { - span_row = m_grid->rows_count() - 1; - } - cell->el->m_pos.y = m_grid->row(row).top + cell->el->content_margins_top(); - cell->el->m_pos.height = m_grid->row(span_row).bottom - m_grid->row(row).top - cell->el->content_margins_top() - cell->el->content_margins_bottom(); - table_height = std::max(table_height, m_grid->row(span_row).bottom); - cell->el->apply_vertical_align(); - } + return m_children.front(); } } + if(create) + { + return add_pseudo_before(style); + } + return nullptr; +} - if (m_border_collapse == border_collapse_collapse) +litehtml::element::ptr litehtml::html_tag::get_element_after(const style& style, bool create) +{ + if(!m_children.empty()) { - if (m_grid->rows_count()) + if( m_children.back()->tag() == __tag_after_ ) { - table_height -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom); + return m_children.back(); } } - else + if(create) { - table_height += m_border_spacing_y; + return add_pseudo_after(style); } - - m_pos.width = table_width; - - calc_auto_margins(parent_width); - - m_pos.move_to(x, y); - m_pos.x += content_margins_left(); - m_pos.y += content_margins_top(); - m_pos.width = table_width; - m_pos.height = table_height; - - return max_table_width; + return nullptr; } -void litehtml::html_tag::draw_children_box(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex) +void litehtml::html_tag::add_style(const style& style) { - position pos = m_pos; - pos.x += x; - pos.y += y; - - document::ptr doc = get_document(); + m_style.combine(style); +} - if (m_overflow > overflow_visible) +void litehtml::html_tag::refresh_styles() +{ + for (auto& el : m_children) { - position border_box = pos; - border_box += m_padding; - border_box += m_borders; - - border_radiuses bdr_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height); - - bdr_radius -= m_borders; - bdr_radius -= m_padding; - - doc->container()->set_clip(pos, bdr_radius, true, true); + if(el->css().get_display() != display_inline_text) + { + el->refresh_styles(); + } } - position browser_wnd; - doc->container()->get_client_rect(browser_wnd); + m_style.clear(); - element::ptr el; - for (auto& item : m_children) + for (auto& usel : m_used_styles) { - el = item; - if (el->is_visible()) + usel->m_used = false; + + if(usel->m_selector->is_media_valid()) { - switch (flag) + int apply = select(*usel->m_selector, false); + + if(apply != select_no_match) { - case draw_positioned: - if (el->is_positioned() && el->get_zindex() == zindex) + if(apply & select_match_pseudo_class) { - if (el->get_element_position() == element_position_fixed) - { - el->draw(hdc, browser_wnd.x, browser_wnd.y, clip); - el->draw_stacking_context(hdc, browser_wnd.x, browser_wnd.y, clip, true); - } - else + if(select(*usel->m_selector, true)) { - el->draw(hdc, pos.x, pos.y, clip); - el->draw_stacking_context(hdc, pos.x, pos.y, clip, true); + if(apply & select_match_with_after) + { + element::ptr el = get_element_after(*usel->m_selector->m_style, false); + if(el) + { + el->add_style(*usel->m_selector->m_style); + } + } else if(apply & select_match_with_before) + { + element::ptr el = get_element_before(*usel->m_selector->m_style, false); + if(el) + { + el->add_style(*usel->m_selector->m_style); + } + } + else + { + add_style(*usel->m_selector->m_style); + usel->m_used = true; + } } - el = 0; - } - break; - case draw_block: - if (!el->is_inline_box() && el->get_float() == float_none && !el->is_positioned()) - { - el->draw(hdc, pos.x, pos.y, clip); - } - break; - case draw_floats: - if (el->get_float() != float_none && !el->is_positioned()) - { - el->draw(hdc, pos.x, pos.y, clip); - el->draw_stacking_context(hdc, pos.x, pos.y, clip, false); - el = 0; - } - break; - case draw_inlines: - if (el->is_inline_box() && el->get_float() == float_none && !el->is_positioned()) + } else if(apply & select_match_with_after) { - el->draw(hdc, pos.x, pos.y, clip); - if (el->get_display() == display_inline_block) + element::ptr el = get_element_after(*usel->m_selector->m_style, false); + if(el) { - el->draw_stacking_context(hdc, pos.x, pos.y, clip, false); - el = 0; + el->add_style(*usel->m_selector->m_style); } - } - break; - default: - break; - } - - if (el) - { - if (flag == draw_positioned) + } else if(apply & select_match_with_before) { - if (!el->is_positioned()) + element::ptr el = get_element_before(*usel->m_selector->m_style, false); + if(el) { - el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex); + el->add_style(*usel->m_selector->m_style); } - } - else + } else { - if (el->get_float() == float_none && - el->get_display() != display_inline_block && - !el->is_positioned()) - { - el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex); - } + add_style(*usel->m_selector->m_style); + usel->m_used = true; } } } } - - if (m_overflow > overflow_visible) - { - doc->container()->del_clip(); - } } -void litehtml::html_tag::draw_children_table(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex) +const litehtml::background* litehtml::html_tag::get_background(bool own_only) { - if (!m_grid) return; - - position pos = m_pos; - pos.x += x; - pos.y += y; - for (int row = 0; row < m_grid->rows_count(); row++) + if(own_only) { - if (flag == draw_block) + // return own background with check for empty one + if(m_css.get_bg().is_empty()) { - m_grid->row(row).el_row->draw_background(hdc, pos.x, pos.y, clip); + return nullptr; } - for (int col = 0; col < m_grid->cols_count(); col++) + return &m_css.get_bg(); + } + + if(m_css.get_bg().is_empty()) + { + // if this is root element (<html>) try to get background from body + if (is_root()) { - table_cell* cell = m_grid->cell(col, row); - if (cell->el) + for (const auto& el : m_children) { - if (flag == draw_block) + if( el->is_body() ) { - cell->el->draw(hdc, pos.x, pos.y, clip); + // return own body background + return el->get_background(true); } - cell->el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex); } } + return nullptr; + } + + if(is_body()) + { + element::ptr el_parent = parent(); + if (el_parent) + { + if (!el_parent->get_background(true)) + { + // parent of body will draw background for body + return nullptr; + } + } + } + + return &m_css.get_bg(); +} + +litehtml::string litehtml::html_tag::dump_get_name() +{ + if(m_tag == empty_id) + { + return "anon [html_tag]"; } + return _s(m_tag) + " [html_tag]"; } diff --git a/src/plugins/litehtml_viewer/litehtml/html_tag.h b/src/plugins/litehtml_viewer/litehtml/html_tag.h @@ -8,232 +8,120 @@ #include "borders.h" #include "css_selector.h" #include "stylesheet.h" -#include "box.h" +#include "line_box.h" #include "table.h" namespace litehtml { - struct line_context - { - int calculatedTop; - int top; - int left; - int right; - - int width() - { - return right - left; - } - void fix_top() - { - calculatedTop = top; - } - }; class html_tag : public element { friend class elements_iterator; friend class el_table; friend class table_grid; - friend class block_box; friend class line_box; public: - typedef std::shared_ptr<litehtml::html_tag> ptr; + typedef std::shared_ptr<html_tag> ptr; protected: - box::vector m_boxes; - string_vector m_class_values; - tstring m_tag; + string_id m_tag; + string_id m_id; + string_vector m_str_classes; + std::vector<string_id> m_classes; litehtml::style m_style; string_map m_attrs; - vertical_align m_vertical_align; - text_align m_text_align; - style_display m_display; - list_style_type m_list_style_type; - list_style_position m_list_style_position; - white_space m_white_space; - element_float m_float; - element_clear m_clear; - floated_box::vector m_floats_left; - floated_box::vector m_floats_right; - elements_vector m_positioned; - background m_bg; - element_position m_el_position; - int m_line_height; - bool m_lh_predefined; - string_vector m_pseudo_classes; - used_selector::vector m_used_styles; - - uint_ptr m_font; - int m_font_size; - font_metrics m_font_metrics; - - css_margins m_css_margins; - css_margins m_css_padding; - css_borders m_css_borders; - css_length m_css_width; - css_length m_css_height; - css_length m_css_min_width; - css_length m_css_min_height; - css_length m_css_max_width; - css_length m_css_max_height; - css_offsets m_css_offsets; - css_length m_css_text_indent; - - overflow m_overflow; - visibility m_visibility; - int m_z_index; - box_sizing m_box_sizing; - - int_int_cache m_cahe_line_left; - int_int_cache m_cahe_line_right; - - // data for table rendering - std::unique_ptr<table_grid> m_grid; - css_length m_css_border_spacing_x; - css_length m_css_border_spacing_y; - int m_border_spacing_x; - int m_border_spacing_y; - border_collapse m_border_collapse; - - virtual void select_all(const css_selector& selector, elements_vector& res) override; + std::vector<string_id> m_pseudo_classes; + + void select_all(const css_selector& selector, elements_vector& res) override; public: - html_tag(const std::shared_ptr<litehtml::document>& doc); - virtual ~html_tag(); - - /* render functions */ - - virtual int render(int x, int y, int max_width, bool second_pass = false) override; - - virtual int render_inline(const element::ptr &container, int max_width) override; - virtual int place_element(const element::ptr &el, int max_width) override; - virtual bool fetch_positioned() override; - virtual void render_positioned(render_type rt = render_all) override; - - int new_box(const element::ptr &el, int max_width, line_context& line_ctx); - - int get_cleared_top(const element::ptr &el, int line_top) const; - int finish_last_box(bool end_of_render = false); - - virtual bool appendChild(const element::ptr &el) override; - virtual bool removeChild(const element::ptr &el) override; - virtual void clearRecursive() override; - virtual const tchar_t* get_tagName() const override; - virtual void set_tagName(const tchar_t* tag) override; - virtual void set_data(const tchar_t* data) override; - virtual element_float get_float() const override; - virtual vertical_align get_vertical_align() const override; - virtual css_length get_css_left() const override; - virtual css_length get_css_right() const override; - virtual css_length get_css_top() const override; - virtual css_length get_css_bottom() const override; - virtual css_length get_css_width() const override; - virtual css_offsets get_css_offsets() const override; - virtual void set_css_width(css_length& w) override; - virtual css_length get_css_height() const override; - virtual element_clear get_clear() const override; - virtual size_t get_children_count() const override; - virtual element::ptr get_child(int idx) const override; - virtual element_position get_element_position(css_offsets* offsets = 0) const override; - virtual overflow get_overflow() const override; - - virtual void set_attr(const tchar_t* name, const tchar_t* val) override; - virtual const tchar_t* get_attr(const tchar_t* name, const tchar_t* def = 0) const override; - virtual void apply_stylesheet(const litehtml::css& stylesheet) override; - virtual void refresh_styles() override; - - virtual bool is_white_space() const override; - virtual bool is_body() const override; - virtual bool is_break() const override; - virtual int get_base_line() override; - virtual bool on_mouse_over() override; - virtual bool on_mouse_leave() override; - virtual bool on_lbutton_down() override; - virtual bool on_lbutton_up() override; - virtual void on_click() override; - virtual bool find_styles_changes(position::vector& redraw_boxes, int x, int y) override; - virtual const tchar_t* get_cursor() override; - virtual void init_font() override; - virtual bool set_pseudo_class(const tchar_t* pclass, bool add) override; - virtual bool set_class(const tchar_t* pclass, bool add) override; - virtual bool is_replaced() const override; - virtual int line_height() const override; - virtual white_space get_white_space() const override; - virtual style_display get_display() const override; - virtual visibility get_visibility() const override; - virtual void parse_styles(bool is_reparse = false) override; - virtual void draw(uint_ptr hdc, int x, int y, const position* clip) override; - virtual void draw_background(uint_ptr hdc, int x, int y, const position* clip) override; - - virtual const tchar_t* get_style_property(const tchar_t* name, bool inherited, const tchar_t* def = 0) override; - virtual uint_ptr get_font(font_metrics* fm = 0) override; - virtual int get_font_size() const override; - - elements_vector& children(); - virtual void calc_outlines(int parent_width) override; - virtual void calc_auto_margins(int parent_width) override; - - virtual int select(const css_selector& selector, bool apply_pseudo = true) override; - virtual int select(const css_element_selector& selector, bool apply_pseudo = true) override; - - virtual elements_vector select_all(const tstring& selector) override; - virtual elements_vector select_all(const css_selector& selector) override; - - virtual element::ptr select_one(const tstring& selector) override; - virtual element::ptr select_one(const css_selector& selector) override; - - virtual element::ptr find_ancestor(const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = 0) override; - virtual element::ptr find_adjacent_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = 0) override; - virtual element::ptr find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = 0) override; - virtual void get_text(tstring& text) override; - virtual void parse_attributes() override; - - virtual bool is_first_child_inline(const element::ptr& el) const override; - virtual bool is_last_child_inline(const element::ptr& el) override; - virtual bool have_inline_child() const override; - virtual void get_content_size(size& sz, int max_width) override; - virtual void init() override; - virtual void get_inline_boxes(position::vector& boxes) override; - virtual bool is_floats_holder() const override; - virtual int get_floats_height(element_float el_float = float_none) const override; - virtual int get_left_floats_height() const override; - virtual int get_right_floats_height() const override; - virtual int get_line_left(int y) override; - virtual int get_line_right(int y, int def_right) override; - virtual void get_line_left_right(int y, int def_right, int& ln_left, int& ln_right) override; - virtual void add_float(const element::ptr &el, int x, int y) override; - virtual void update_floats(int dy, const element::ptr &parent) override; - virtual void add_positioned(const element::ptr &el) override; - virtual int find_next_line_top(int top, int width, int def_right) override; - virtual void apply_vertical_align() override; - virtual void draw_children(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex) override; - virtual int get_zindex() const override; - virtual void draw_stacking_context(uint_ptr hdc, int x, int y, const position* clip, bool with_positioned) override; - virtual void calc_document_size(litehtml::size& sz, int x = 0, int y = 0) override; - virtual void get_redraw_box(litehtml::position& pos, int x = 0, int y = 0) override; - virtual void add_style(const litehtml::style& st) override; - virtual element::ptr get_element_by_point(int x, int y, int client_x, int client_y) override; - virtual element::ptr get_child_by_point(int x, int y, int client_x, int client_y, draw_flag flag, int zindex) override; - - virtual bool is_nth_child(const element::ptr& el, int num, int off, bool of_type) const override; - virtual bool is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const override; - virtual bool is_only_child(const element::ptr& el, bool of_type) const override; - virtual const background* get_background(bool own_only = false) override; + explicit html_tag(const std::shared_ptr<document>& doc); + // constructor for anonymous wrapper boxes + explicit html_tag(const element::ptr& parent, const string& style = "display: block"); + + bool appendChild(const element::ptr &el) override; + bool removeChild(const element::ptr &el) override; + void clearRecursive() override; + string_id tag() const override; + string_id id() const override; + const char* get_tagName() const override; + void set_tagName(const char* tag) override; + void set_data(const char* data) override; + size_t get_children_count() const override; + element::ptr get_child(int idx) const override; + + void set_attr(const char* name, const char* val) override; + const char* get_attr(const char* name, const char* def = nullptr) const override; + void apply_stylesheet(const litehtml::css& stylesheet) override; + void refresh_styles() override; + + bool is_white_space() const override; + bool is_body() const override; + bool is_break() const override; + + bool on_mouse_over() override; + bool on_mouse_leave() override; + bool on_lbutton_down() override; + bool on_lbutton_up() override; + void on_click() override; + bool set_pseudo_class(string_id cls, bool add) override; + bool set_class(const char* pclass, bool add) override; + bool is_replaced() const override; + void compute_styles(bool recursive = true) override; + void draw(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) override; + void draw_background(uint_ptr hdc, int x, int y, const position *clip, + const std::shared_ptr<render_item> &ri) override; + + 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; + 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; + float get_number_property(string_id name, bool inherited, float default_value, uint_ptr css_properties_member_offset) const override; + string_vector get_string_vector_property(string_id name, bool inherited, const string_vector& default_value, uint_ptr css_properties_member_offset) const override; + int_vector get_int_vector_property (string_id name, bool inherited, const int_vector& default_value, uint_ptr css_properties_member_offset) const override; + length_vector get_length_vector_property(string_id name, bool inherited, const length_vector& default_value, uint_ptr css_properties_member_offset) const override; + size_vector get_size_vector_property (string_id name, bool inherited, const size_vector& default_value, uint_ptr css_properties_member_offset) const override; + string get_custom_property(string_id name, const string& default_value) const override; + + elements_vector& children(); + + int select(const string& selector) override; + int select(const css_selector& selector, bool apply_pseudo = true) override; + int select(const css_element_selector& selector, bool apply_pseudo = true) override; + int select_pseudoclass(const css_attribute_selector& sel); + int select_attribute(const css_attribute_selector& sel); + + elements_vector select_all(const string& selector) override; + elements_vector select_all(const css_selector& selector) override; + + element::ptr select_one(const string& selector) override; + element::ptr select_one(const css_selector& selector) override; + + element::ptr find_ancestor(const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = nullptr) override; + element::ptr find_adjacent_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = nullptr) override; + element::ptr find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = nullptr) override; + void get_text(string& text) override; + void parse_attributes() override; + + void get_content_size(size& sz, int max_width) override; + bool is_floats_holder() const override; + void add_style(const style& style) override; + + bool is_nth_child(const element::ptr& el, int num, int off, bool of_type) const override; + bool is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const override; + bool is_only_child(const element::ptr& el, bool of_type) const override; + const background* get_background(bool own_only = false) override; + + string dump_get_name() override; protected: - void draw_children_box(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex); - void draw_children_table(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex); - int render_box(int x, int y, int max_width, bool second_pass = false); - int render_table(int x, int y, int max_width, bool second_pass = false); - int fix_line_width(int max_width, element_float flt); - void parse_background(); - void init_background_paint( position pos, background_paint &bg_paint, const background* bg ); - void draw_list_marker( uint_ptr hdc, const position &pos ); - tstring get_list_marker_text(int index); - void parse_nth_child_params( tstring param, int &num, int &off ); - void remove_before_after(); - litehtml::element::ptr get_element_before(); - litehtml::element::ptr get_element_after(); + void init_background_paint(position pos, std::vector<background_paint>& bg_paint, const background* bg, const std::shared_ptr<render_item>& ri); + void init_one_background_paint(int i, position pos, background_paint& bg_paint, const background* bg, const std::shared_ptr<render_item>& ri); + void draw_list_marker( uint_ptr hdc, const position &pos ); + 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); }; /************************************************************************/ diff --git a/src/plugins/litehtml_viewer/litehtml/iterators.cpp b/src/plugins/litehtml_viewer/litehtml/iterators.cpp @@ -1,92 +1,99 @@ #include "html.h" #include "iterators.h" #include "html_tag.h" +#include "render_item.h" +#include <iterator> -litehtml::element::ptr litehtml::elements_iterator::next(bool ret_parent) +litehtml::elements_iterator::elements_iterator(bool return_parents, iterator_selector* go_inside, iterator_selector* select) : + m_return_parent(return_parents), + m_go_inside(go_inside), + m_select(select) { - next_idx(); - - while(m_idx < (int) m_el->get_children_count()) - { - element::ptr el = m_el->get_child(m_idx); - if( el->get_children_count() && m_go_inside && m_go_inside->select(el) ) - { - stack_item si; - si.idx = m_idx; - si.el = m_el; - m_stack.push_back(si); - m_el = el; - m_idx = -1; - if(ret_parent) - { - return el; - } - next_idx(); - } else - { - if( !m_select || (m_select && m_select->select(m_el->get_child(m_idx))) ) - { - return m_el->get_child(m_idx); - } else - { - next_idx(); - } - } - } +} - return 0; +bool litehtml::elements_iterator::go_inside(const std::shared_ptr<render_item>& el) +{ + return /*!el->children().empty() &&*/ m_go_inside && m_go_inside->select(el); } -void litehtml::elements_iterator::next_idx() +void litehtml::elements_iterator::process(const std::shared_ptr<render_item>& container, const std::function<void (std::shared_ptr<render_item>&, iterator_item_type)>& func) { - m_idx++; - while(m_idx >= (int) m_el->get_children_count() && m_stack.size()) - { - stack_item si = m_stack.back(); - m_stack.pop_back(); - m_idx = si.idx; - m_el = si.el; - m_idx++; - continue; - } + for(auto& el : container->children()) + { + if(go_inside(el)) + { + if(m_return_parent) + { + // call function for parent + func(el, iterator_item_type_start_parent); + } + // go inside element and process its items + process(el, func); + if(m_return_parent) + { + // call function for parent + func(el, iterator_item_type_end_parent); + } + } else + { + // call function for element + if(!m_select || m_select->select(el)) + { + func(el, iterator_item_type_child); + } + } + } } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// -bool litehtml::go_inside_inline::select(const element::ptr& el) +bool litehtml::go_inside_inline::select(const std::shared_ptr<render_item>& el) { - if(el->get_display() == display_inline || el->get_display() == display_inline_text) + if(el->src_el()->css().get_display() == display_inline && el->src_el()->css().get_float() == float_none) { return true; } return false; } -bool litehtml::go_inside_table::select(const element::ptr& el) +bool litehtml::inline_selector::select(const std::shared_ptr<render_item>& el) +{ + if(el->src_el()->css().get_display() == display_inline_text || + el->src_el()->css().get_display() == display_inline_table || + el->src_el()->css().get_display() == display_inline_block || + el->src_el()->css().get_display() == display_inline_flex || + el->src_el()->css().get_float() != float_none) + { + return true; + } + return false; +} + +bool litehtml::go_inside_table::select(const std::shared_ptr<render_item>& el) { - if( el->get_display() == display_table_row_group || - el->get_display() == display_table_header_group || - el->get_display() == display_table_footer_group) + if( el->src_el()->css().get_display() == display_table_row_group || + el->src_el()->css().get_display() == display_table_header_group || + el->src_el()->css().get_display() == display_table_footer_group) { return true; } return false; } -bool litehtml::table_rows_selector::select(const element::ptr& el) +bool litehtml::table_rows_selector::select(const std::shared_ptr<render_item>& el) { - if( el->get_display() == display_table_row) + if( el->src_el()->css().get_display() == display_table_row) { return true; } return false; } -bool litehtml::table_cells_selector::select(const element::ptr& el) +bool litehtml::table_cells_selector::select(const std::shared_ptr<render_item>& el) { - if( el->get_display() == display_table_cell) + if( el->src_el()->css().get_display() == display_table_cell) { return true; } diff --git a/src/plugins/litehtml_viewer/litehtml/iterators.h b/src/plugins/litehtml_viewer/litehtml/iterators.h @@ -2,88 +2,82 @@ #define LH_ITERATORS_H #include "types.h" +#include <list> +#include <functional> namespace litehtml { - class element; + class render_item; class iterator_selector { public: - virtual bool select(const element::ptr& el) = 0; + virtual bool select(const std::shared_ptr<render_item>& el) = 0; + + protected: + ~iterator_selector() = default; + }; + + enum iterator_item_type + { + iterator_item_type_child, + iterator_item_type_start_parent, + iterator_item_type_end_parent }; class elements_iterator { private: - struct stack_item - { - int idx; - element::ptr el; - stack_item() - { + iterator_selector* m_go_inside; + iterator_selector* m_select; + bool m_return_parent; - } - stack_item(const stack_item& val) - { - idx = val.idx; - el = val.el; - } - stack_item(stack_item&& val) - { - idx = val.idx; - el = std::move(val.el); - } - }; + /** + * Checks if iterator should go inside the element + * + * @param el element to check + * @return true to go inside + */ + bool go_inside(const std::shared_ptr<render_item>& el); - std::vector<stack_item> m_stack; - element::ptr m_el; - int m_idx; - iterator_selector* m_go_inside; - iterator_selector* m_select; public: + elements_iterator(bool return_parents, iterator_selector* go_inside, iterator_selector* select); + ~elements_iterator() = default; - elements_iterator(const element::ptr& el, iterator_selector* go_inside, iterator_selector* select) - { - m_el = el; - m_idx = -1; - m_go_inside = go_inside; - m_select = select; - } - - ~elements_iterator() - { - - } - - element::ptr next(bool ret_parent = true); + void process(const std::shared_ptr<render_item>& container, const std::function<void (std::shared_ptr<render_item>&, iterator_item_type)>& func); private: void next_idx(); }; - class go_inside_inline : public iterator_selector + class go_inside_inline final : public iterator_selector { public: - virtual bool select(const element::ptr& el); + bool select(const std::shared_ptr<render_item>& el) override; }; - class go_inside_table : public iterator_selector + class inline_selector final : public iterator_selector + { + public: + bool select(const std::shared_ptr<render_item>& el) override; + }; + + class go_inside_table final : public iterator_selector { public: - virtual bool select(const element::ptr& el); + bool select(const std::shared_ptr<render_item>& el) override; }; - class table_rows_selector : public iterator_selector + class table_rows_selector final : public iterator_selector { public: - virtual bool select(const element::ptr& el); + bool select(const std::shared_ptr<render_item>& el) override; }; - class table_cells_selector : public iterator_selector + class table_cells_selector final : public iterator_selector { public: - virtual bool select(const element::ptr& el); + bool select(const std::shared_ptr<render_item>& el) override; }; } diff --git a/src/plugins/litehtml_viewer/litehtml/line_box.cpp b/src/plugins/litehtml_viewer/litehtml/line_box.cpp @@ -0,0 +1,698 @@ +#include "html.h" +#include "line_box.h" +#include "element.h" +#include "render_item.h" +#include <algorithm> + +////////////////////////////////////////////////////////////////////////////////////////// + +void litehtml::line_box_item::place_to(int x, int y) +{ + m_element->pos().x = x + m_element->content_offset_left(); + m_element->pos().y = y + m_element->content_offset_top(); +} + +litehtml::position& litehtml::line_box_item::pos() +{ + return m_element->pos(); +} + + +int litehtml::line_box_item::width() const +{ + return m_element->width(); +} + +int litehtml::line_box_item::top() const +{ + return m_element->top(); +} + +int litehtml::line_box_item::bottom() const +{ + return m_element->bottom(); +} + +int litehtml::line_box_item::right() const +{ + return m_element->right(); +} + +int litehtml::line_box_item::left() const +{ + return m_element->left(); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +litehtml::lbi_start::lbi_start(const std::shared_ptr<render_item>& element) : line_box_item(element) +{ + m_pos.height = m_element->src_el()->css().get_font_metrics().height; + m_pos.width = m_element->content_offset_left(); +} + +void litehtml::lbi_start::place_to(int x, int y) +{ + m_pos.x = x + m_element->content_offset_left(); + m_pos.y = y; +} + +int litehtml::lbi_start::width() const +{ + return m_pos.width; +} + +int litehtml::lbi_start::top() const +{ + return m_pos.y; +} + +int litehtml::lbi_start::bottom() const +{ + return m_pos.y + m_pos.height; +} + +int litehtml::lbi_start::right() const +{ + return m_pos.x; +} + +int litehtml::lbi_start::left() const +{ + return m_pos.x - m_element->content_offset_left(); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +litehtml::lbi_end::lbi_end(const std::shared_ptr<render_item>& element) : lbi_start(element) +{ + m_pos.height = m_element->src_el()->css().get_font_metrics().height; + m_pos.width = m_element->content_offset_right(); +} + +void litehtml::lbi_end::place_to(int x, int y) +{ + m_pos.x = x; + m_pos.y = y; +} + +int litehtml::lbi_end::right() const +{ + return m_pos.x + m_pos.width; +} + +int litehtml::lbi_end::left() const +{ + return m_pos.x; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +litehtml::lbi_continue::lbi_continue(const std::shared_ptr<render_item>& element) : lbi_start(element) +{ + m_pos.height = m_element->src_el()->css().get_font_metrics().height; + m_pos.width = 0; +} + +void litehtml::lbi_continue::place_to(int x, int y) +{ + m_pos.x = x; + m_pos.y = y; +} + +int litehtml::lbi_continue::right() const +{ + return m_pos.x; +} + +int litehtml::lbi_continue::left() const +{ + return m_pos.x; +} + +int litehtml::lbi_continue::width() const +{ + return 0; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +void litehtml::line_box::add_item(std::unique_ptr<line_box_item> item) +{ + item->get_el()->skip(false); + bool add = true; + switch (item->get_type()) + { + case line_box_item::type_text_part: + if(item->get_el()->src_el()->is_white_space()) + { + add = !is_empty() && !have_last_space(); + } + break; + case line_box_item::type_inline_start: + case line_box_item::type_inline_end: + case line_box_item::type_inline_continue: + add = true; + break; + } + if(add) + { + item->place_to(m_left + m_width, m_top); + m_width += item->width(); + m_height = std::max(m_height, item->get_el()->height()); + m_items.emplace_back(std::move(item)); + } else + { + item->get_el()->skip(true); + } +} + +int litehtml::line_box::calc_va_baseline(const va_context& current, vertical_align va, const font_metrics& new_font, int top, int bottom) +{ + switch(va) + { + case va_super: + return current.baseline - current.fm.height / 3; + case va_sub: + return current.baseline + current.fm.height / 3; + case va_middle: + return current.baseline - current.fm.x_height / 2; + case va_text_top: + return current.baseline - (current.fm.height - current.fm.base_line()) + + new_font.height - new_font.base_line(); + case va_text_bottom: + return current.baseline + current.fm.base_line() - new_font.base_line(); + case va_top: + return top + new_font.height - new_font.base_line(); + case va_bottom: + return bottom - new_font.height + new_font.base_line(); + default: + return current.baseline; + } +} + +std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::finish(bool last_box, const containing_block_context &containing_block_size) +{ + std::list< std::unique_ptr<line_box_item> > ret_items; + + if(!last_box) + { + while(!m_items.empty()) + { + if (m_items.back()->get_type() == line_box_item::type_text_part) + { + // remove trailing spaces + if (m_items.back()->get_el()->src_el()->is_break() || + m_items.back()->get_el()->src_el()->is_white_space()) + { + m_width -= m_items.back()->width(); + m_items.back()->get_el()->skip(true); + m_items.pop_back(); + } else + { + break; + } + } else if (m_items.back()->get_type() == line_box_item::type_inline_start) + { + // remove trailing empty inline_start markers + // these markers will be added at the beginning of the next line box + m_width -= m_items.back()->width(); + ret_items.emplace_back(std::move(m_items.back())); + m_items.pop_back(); + } else + { + break; + } + } + } else + { + // remove trailing spaces + auto iter = m_items.rbegin(); + while(iter != m_items.rend()) + { + if ((*iter)->get_type() == line_box_item::type_text_part) + { + if((*iter)->get_el()->src_el()->is_white_space()) + { + (*iter)->get_el()->skip(true); + m_width -= (*iter)->width(); + // Space can be between text and inline_end marker + // We have to shift all items on the right side + if(iter != m_items.rbegin()) + { + auto r_iter = iter; + r_iter--; + while (true) + { + (*r_iter)->pos().x -= (*iter)->width(); + if (r_iter == m_items.rbegin()) + { + break; + } + r_iter--; + } + } + // erase white space element + iter = decltype(iter) (m_items.erase( std::next(iter).base() )); + } else + { + break; + } + } else + { + iter++; + } + } + } + + if( is_empty() || (!is_empty() && last_box && is_break_only()) ) + { + m_height = m_default_line_height; + m_baseline = m_font_metrics.base_line(); + return ret_items; + } + + int spc_x = 0; + + int add_x = 0; + switch(m_text_align) + { + case text_align_right: + if(m_width < (m_right - m_left)) + { + add_x = (m_right - m_left) - m_width; + } + break; + case text_align_center: + if(m_width < (m_right - m_left)) + { + add_x = ((m_right - m_left) - m_width) / 2; + } + break; + case text_align_justify: + if (m_width < (m_right - m_left)) + { + add_x = 0; + spc_x = (m_right - m_left) - m_width; + if (spc_x > m_width/4) + spc_x = 0; + } + break; + default: + add_x = 0; + } + + int counter = 0; + float offj = float(spc_x) / std::max(1.f, float(m_items.size())-1.f); + float cixx = 0.0f; + + int line_top = 0; + int line_bottom = 0; + + va_context current_context; + std::list<va_context> contexts; + + current_context.baseline = 0; + current_context.fm = m_font_metrics; + + m_min_width = 0; + + for (const auto& lbi : m_items) + { + m_min_width += lbi->get_rendered_min_width(); + { // start text_align_justify + if (spc_x && counter) + { + cixx += offj; + if ((counter + 1) == int(m_items.size())) + cixx += 0.99f; + lbi->pos().x += int(cixx); + } + counter++; + if ((m_text_align == text_align_right || spc_x) && counter == int(m_items.size())) + { + // Forcible justify the last element to the right side for text align right and justify; + lbi->pos().x = m_right - lbi->pos().width; + } else if (add_x) + { + lbi->pos().x += add_x; + } + } // end text_align_justify + + if (lbi->get_type() == line_box_item::type_inline_start || lbi->get_type() == line_box_item::type_inline_continue) + { + contexts.push_back(current_context); + current_context.baseline = calc_va_baseline(current_context, + lbi->get_el()->css().get_vertical_align(), + lbi->get_el()->css().get_font_metrics(), + line_top, line_bottom); + current_context.fm = lbi->get_el()->css().get_font_metrics(); + } + + // Align elements vertically by baseline. + if(lbi->get_el()->src_el()->css().get_display() == display_inline_text || lbi->get_el()->src_el()->css().get_display() == display_inline) + { + // inline elements and text are aligned by baseline only + // at this point the baseline for text is properly aligned already + lbi->pos().y = current_context.baseline - lbi->get_el()->css().get_font_metrics().height + lbi->get_el()->css().get_font_metrics().base_line(); + } else + { + switch(lbi->get_el()->css().get_vertical_align()) + { + case va_sub: + 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->get_el()->content_offset_top(); + } + break; + case va_bottom: + lbi->pos().y = line_bottom - lbi->get_el()->height() + lbi->get_el()->content_offset_top(); + break; + case va_top: + 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->get_el()->content_offset_top(); + break; + case va_text_top: + lbi->pos().y = current_context.baseline - current_context.fm.height + current_context.fm.base_line() + + lbi->get_el()->content_offset_top(); + break; + case va_text_bottom: + lbi->pos().y = current_context.baseline + current_context.fm.base_line() - lbi->get_el()->height() + + lbi->get_el()->content_offset_top(); + break; + case va_middle: + lbi->pos().y = current_context.baseline - current_context.fm.x_height / 2 - lbi->get_el()->height() / 2 + + lbi->get_el()->content_offset_top(); + break; + } + } + + if (lbi->get_type() == line_box_item::type_inline_end) + { + if(!contexts.empty()) + { + current_context = contexts.back(); + contexts.pop_back(); + } + } + + // calculate line height + line_top = std::min(line_top, lbi->top()); + line_bottom = std::max(line_bottom, lbi->bottom()); + + if(lbi->get_el()->src_el()->css().get_display() == display_inline_text) + { + m_line_height = std::max(m_line_height, lbi->get_el()->css().get_line_height()); + } + } + + m_height = line_bottom - line_top; + int top_shift = line_top; + if(m_height < m_line_height) + { + top_shift -= (m_line_height - m_height) / 2; + m_height = m_line_height; + } + m_baseline = line_bottom; + + struct inline_item_box + { + std::shared_ptr<render_item> element; + position box; + + inline_item_box() = default; + explicit inline_item_box(const std::shared_ptr<render_item>& el) : element(el) {} + }; + + std::list<inline_item_box> inlines; + + contexts.clear(); + + current_context.baseline = 0; + current_context.fm = m_font_metrics; + bool va_top_bottom = false; + + for (const auto& lbi : m_items) + { + // Calculate baseline. Now we calculate baseline for vertical alignment top and bottom + if (lbi->get_type() == line_box_item::type_inline_start || lbi->get_type() == line_box_item::type_inline_continue) + { + contexts.push_back(current_context); + va_top_bottom = lbi->get_el()->css().get_vertical_align() == va_bottom || lbi->get_el()->css().get_vertical_align() == va_top; + current_context.baseline = calc_va_baseline(current_context, + lbi->get_el()->css().get_vertical_align(), + lbi->get_el()->css().get_font_metrics(), + top_shift, top_shift + m_height); + current_context.fm = lbi->get_el()->css().get_font_metrics(); + } + + // Align inlines and text by baseline if current vertical alignment is top or bottom + if(va_top_bottom) + { + if (lbi->get_el()->src_el()->css().get_display() == display_inline_text || + lbi->get_el()->src_el()->css().get_display() == display_inline) + { + // inline elements and text are aligned by baseline only + // at this point the baseline for text is properly aligned already + lbi->pos().y = current_context.baseline - lbi->get_el()->css().get_font_metrics().height + + lbi->get_el()->css().get_font_metrics().base_line(); + } + } + + // Pop the prev context + if (lbi->get_type() == line_box_item::type_inline_end) + { + if(!contexts.empty()) + { + current_context = contexts.back(); + contexts.pop_back(); + } + } + + // move element to the correct position + lbi->pos().y += m_top - top_shift; + + // Perform vertical align top and bottom for inline boxes + if(lbi->get_el()->css().get_display() != display_inline_text && lbi->get_el()->css().get_display() != display_inline) + { + if(lbi->get_el()->css().get_vertical_align() == va_top) + { + lbi->pos().y = m_top + lbi->get_el()->content_offset_top(); + } else if(lbi->get_el()->css().get_vertical_align() == va_bottom) + { + lbi->pos().y = m_top + m_height - lbi->get_el()->height() + lbi->get_el()->content_offset_top(); + } + } + lbi->get_el()->apply_relative_shift(containing_block_size); + + // Calculate and push inline box into the render item element + if(lbi->get_type() == line_box_item::type_inline_start || lbi->get_type() == line_box_item::type_inline_continue) + { + if(lbi->get_type() == line_box_item::type_inline_start) + { + lbi->get_el()->clear_inline_boxes(); + } + inlines.emplace_back(lbi->get_el()); + inlines.back().box.x = lbi->left(); + inlines.back().box.y = lbi->top() - lbi->get_el()->content_offset_top(); + inlines.back().box.height = lbi->bottom() - lbi->top() + lbi->get_el()->content_offset_height(); + } else if(lbi->get_type() == line_box_item::type_inline_end) + { + if(!inlines.empty()) + { + inlines.back().box.width = lbi->right() - inlines.back().box.x; + inlines.back().element->add_inline_box(inlines.back().box); + inlines.pop_back(); + } + } + } + + for(auto iter = inlines.rbegin(); iter != inlines.rend(); ++iter) + { + iter->box.width = m_items.back()->right() - iter->box.x; + iter->element->add_inline_box(iter->box); + + ret_items.emplace_front(std::unique_ptr<line_box_item>(new lbi_continue(iter->element))); + } + + return std::move(ret_items); +} + +std::shared_ptr<litehtml::render_item> litehtml::line_box::get_first_text_part() const +{ + for(const auto & item : m_items) + { + if(item->get_type() == line_box_item::type_text_part) + { + return item->get_el(); + } + } + return nullptr; +} + + +std::shared_ptr<litehtml::render_item> litehtml::line_box::get_last_text_part() const +{ + for(auto iter = m_items.rbegin(); iter != m_items.rend(); iter++) + { + if((*iter)->get_type() == line_box_item::type_text_part) + { + return (*iter)->get_el(); + } + } + return nullptr; +} + + +bool litehtml::line_box::can_hold(const std::unique_ptr<line_box_item>& item, white_space ws) const +{ + if(!item->get_el()->src_el()->is_inline_box()) return false; + + if(item->get_type() == line_box_item::type_text_part) + { + auto last_el = get_last_text_part(); + + // force new line if the last placed element was line break + if (last_el && last_el->src_el()->is_break()) + { + return false; + } + + // line break should stay in current line box + if (item->get_el()->src_el()->is_break()) + { + return true; + } + + if (ws == white_space_nowrap || ws == white_space_pre || + (ws == white_space_pre_wrap && item->get_el()->src_el()->is_space())) + { + return true; + } + + if (m_left + m_width + item->width() > m_right) + { + return false; + } + } + + return true; +} + +bool litehtml::line_box::have_last_space() const +{ + auto last_el = get_last_text_part(); + if(last_el) + { + return last_el->src_el()->is_white_space() || last_el->src_el()->is_break(); + } + return false; +} + +bool litehtml::line_box::is_empty() const +{ + if(m_items.empty()) return true; + for (const auto& el : m_items) + { + if(el->get_type() == line_box_item::type_text_part) + { + if (!el->get_el()->skip() || el->get_el()->src_el()->is_break()) + { + return false; + } + } + } + return true; +} + +int litehtml::line_box::baseline() const +{ + return m_baseline; +} + +int litehtml::line_box::top_margin() const +{ + return 0; +} + +int litehtml::line_box::bottom_margin() const +{ + return 0; +} + +void litehtml::line_box::y_shift( int shift ) +{ + m_top += shift; + for (auto& el : m_items) + { + el->pos().y += shift; + } +} + +bool litehtml::line_box::is_break_only() const +{ + if(m_items.empty()) return false; + + bool break_found = false; + + for (auto iter = m_items.rbegin(); iter != m_items.rend(); iter++) + { + if((*iter)->get_type() == line_box_item::type_text_part) + { + if((*iter)->get_el()->src_el()->is_break()) + { + break_found = true; + } else if(!(*iter)->get_el()->skip()) + { + return false; + } + } + } + return break_found; +} + +std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::new_width( int left, int right) +{ + std::list< std::unique_ptr<line_box_item> > ret_items; + int add = left - m_left; + if(add) + { + m_left = left; + m_right = right; + m_width = 0; + auto remove_begin = m_items.end(); + auto i = m_items.begin(); + i++; + while (i != m_items.end()) + { + if(!(*i)->get_el()->skip()) + { + if(m_left + m_width + (*i)->width() > m_right) + { + remove_begin = i; + break; + } else + { + (*i)->pos().x += add; + m_width += (*i)->get_el()->width(); + } + } + i++; + } + if(remove_begin != m_items.end()) + { + while(remove_begin != m_items.end()) + { + ret_items.emplace_back(std::move(*remove_begin)); + } + m_items.erase(remove_begin, m_items.end()); + } + } + return ret_items; +} + diff --git a/src/plugins/litehtml_viewer/litehtml/line_box.h b/src/plugins/litehtml_viewer/litehtml/line_box.h @@ -0,0 +1,170 @@ +#ifndef LH_LINE_BOX_H +#define LH_LINE_BOX_H + +#include <vector> +#include <memory> +#include "os_types.h" +#include "types.h" + +namespace litehtml +{ + class render_item; + + struct line_context + { + int calculatedTop; + int top; + int left; + int right; + + int width() const + { + return right - left; + } + void fix_top() + { + calculatedTop = top; + } + }; + + class line_box_item + { + public: + enum element_type + { + type_text_part, + type_inline_start, + type_inline_continue, + type_inline_end + }; + protected: + std::shared_ptr<render_item> m_element; + int m_rendered_min_width; + public: + explicit line_box_item(const std::shared_ptr<render_item>& element) : m_element(element), m_rendered_min_width(0) {} + line_box_item() = default; + line_box_item(const line_box_item& el) = default; + line_box_item(line_box_item&&) = default; + + int height() const { return right() - left(); } + const std::shared_ptr<render_item>& get_el() const { return m_element; } + virtual position& pos(); + virtual void place_to(int x, int y); + virtual int width() const; + virtual int top() const; + virtual int bottom() const; + virtual int right() const; + virtual int left() const; + virtual element_type get_type() const { return type_text_part; } + virtual int get_rendered_min_width() const { return m_rendered_min_width; } + virtual void set_rendered_min_width(int min_width) { m_rendered_min_width = min_width; } + }; + + class lbi_start : public line_box_item + { + protected: + position m_pos; + public: + explicit lbi_start(const std::shared_ptr<render_item>& element); + + void place_to(int x, int y) override; + int width() const override; + position& pos() override { return m_pos; } + int top() const override; + int bottom() const override; + int right() const override; + int left() const override; + element_type get_type() const override { return type_inline_start; } + int get_rendered_min_width() const override { return width(); } + }; + + class lbi_end : public lbi_start + { + public: + explicit lbi_end(const std::shared_ptr<render_item>& element); + + void place_to(int x, int y) override; + int right() const override; + int left() const override; + element_type get_type() const override { return type_inline_end; } + }; + + class lbi_continue : public lbi_start + { + public: + explicit lbi_continue(const std::shared_ptr<render_item>& element); + + void place_to(int x, int y) override; + int right() const override; + int left() const override; + int width() const override; + element_type get_type() const override { return type_inline_continue; } + }; + + class line_box + { + struct va_context + { + int baseline; + font_metrics fm; + + va_context() : baseline(0) {} + }; + + int m_top; + int m_left; + int m_right; + int m_height; + int m_width; + int m_line_height; + int m_default_line_height; + font_metrics m_font_metrics; + int m_baseline; + text_align m_text_align; + int m_min_width; + std::list< std::unique_ptr<line_box_item> > m_items; + public: + line_box(int top, int left, int right, int line_height, const font_metrics& fm, text_align align) : + m_top(top), + m_left(left), + m_right(right), + m_height(0), + m_width(0), + m_font_metrics(fm), + m_default_line_height(line_height), + m_baseline(0), + m_line_height(0), + m_text_align(align), + m_min_width(0) + { + } + + int bottom() const { return m_top + height(); } + int top() const { return m_top; } + int right() const { return m_left + width(); } + int left() const { return m_left; } + int height() const { return m_height; } + int width() const { return m_width; } + int line_right() const { return m_right; } + int min_width() const { return m_min_width; } + + void add_item(std::unique_ptr<line_box_item> item); + bool can_hold(const std::unique_ptr<line_box_item>& item, white_space ws) const; + bool is_empty() const; + int baseline() const; + int top_margin() const; + int bottom_margin() const; + void y_shift(int shift); + std::list< std::unique_ptr<line_box_item> > finish(bool last_box, const containing_block_context &containing_block_size); + std::list< std::unique_ptr<line_box_item> > new_width(int left, int right); + std::shared_ptr<render_item> get_last_text_part() const; + std::shared_ptr<render_item> get_first_text_part() const; + std::list< std::unique_ptr<line_box_item> >& items() { return m_items; } + private: + bool have_last_space() const; + bool is_break_only() const; + static int calc_va_baseline(const va_context& current, vertical_align va, const font_metrics& new_font, int top, int bottom); + }; +} + +#endif //LH_LINE_BOX_H diff --git a/src/plugins/litehtml_viewer/litehtml/litehtml.h b/src/plugins/litehtml_viewer/litehtml/litehtml.h @@ -6,5 +6,6 @@ #include <litehtml/html_tag.h> #include <litehtml/stylesheet.h> #include <litehtml/element.h> +#include <litehtml/utf8_strings.h> #endif // LITEHTML_H diff --git a/src/plugins/litehtml_viewer/litehtml/master_css.h b/src/plugins/litehtml_viewer/litehtml/master_css.h @@ -0,0 +1,351 @@ +#ifndef LH_MASTER_CSS_H +#define LH_MASTER_CSS_H +namespace litehtml{ const char* const master_css = R"##( + + +html { + display: block; + position: relative; +} + +head { + display: none +} + +meta { + display: none +} + +title { + display: none +} + +link { + display: none +} + +style { + display: none +} + +script { + display: none +} + +body { + display:block; + margin:8px; +} + +p { + display:block; + margin-top:1em; + margin-bottom:1em; +} + +b, strong { + display:inline; + font-weight:bold; +} + +i, em, cite { + display:inline; + font-style:italic; +} + +ins, u { + text-decoration:underline +} + +del, s, strike { + text-decoration:line-through +} + +center +{ + text-align:center; + display:block; +} + +a:link +{ + text-decoration: underline; + color: #00f; + cursor: pointer; +} + +h1, h2, h3, h4, h5, h6, div { + display:block; +} + +h1 { + font-weight:bold; + margin-top:0.67em; + margin-bottom:0.67em; + font-size: 2em; +} + +h2 { + font-weight:bold; + margin-top:0.83em; + margin-bottom:0.83em; + font-size: 1.5em; +} + +h3 { + font-weight:bold; + margin-top:1em; + margin-bottom:1em; + font-size:1.17em; +} + +h4 { + font-weight:bold; + margin-top:1.33em; + margin-bottom:1.33em +} + +h5 { + font-weight:bold; + margin-top:1.67em; + margin-bottom:1.67em; + font-size:.83em; +} + +h6 { + font-weight:bold; + margin-top:2.33em; + margin-bottom:2.33em; + font-size:.67em; +} + +br { + display:inline-block; +} + +br[clear="all"] +{ + clear:both; +} + +br[clear="left"] +{ + clear:left; +} + +br[clear="right"] +{ + clear:right; +} + +span { + display:inline +} + +img { + display: inline-block; +} + +img[align="right"] +{ + float: right; +} + +img[align="left"] +{ + float: left; +} + +hr { + display: block; + margin-top: 0.5em; + margin-bottom: 0.5em; + margin-left: auto; + margin-right: auto; + border-style: inset; + border-width: 1px +} + + +/***************** TABLES ********************/ + +table { + display: table; + border-collapse: separate; + border-spacing: 2px; + border-top-color:gray; + border-left-color:gray; + border-bottom-color:black; + border-right-color:black; + font-size: medium; + font-weight: normal; + font-style: normal; +} + +tbody, tfoot, thead { + display:table-row-group; + vertical-align:middle; +} + +tr { + display: table-row; + vertical-align: inherit; + border-color: inherit; +} + +td, th { + display: table-cell; + vertical-align: inherit; + border-width:1px; + padding:1px; +} + +th { + font-weight: bold; +} + +table[border] { + border-style:solid; +} + +table[border|=0] { + border-style:none; +} + +table[border] td, table[border] th { + border-style:solid; + border-top-color:black; + border-left-color:black; + border-bottom-color:gray; + border-right-color:gray; +} + +table[border|=0] td, table[border|=0] th { + border-style:none; +} + +caption { + display: table-caption; +} + +td[nowrap], th[nowrap] { + white-space:nowrap; +} + +tt, code, kbd, samp { + font-family: monospace +} + +pre, xmp, plaintext, listing { + display: block; + font-family: monospace; + white-space: pre; + margin: 1em 0 +} + +/***************** LISTS ********************/ + +ul, menu, dir { + display: block; + list-style-type: disc; + margin-top: 1em; + margin-bottom: 1em; + margin-left: 0; + margin-right: 0; + padding-left: 40px +} + +ol { + display: block; + list-style-type: decimal; + margin-top: 1em; + margin-bottom: 1em; + margin-left: 0; + margin-right: 0; + padding-left: 40px +} + +li { + display: list-item; +} + +ul ul, ol ul { + list-style-type: circle; +} + +ol ol ul, ol ul ul, ul ol ul, ul ul ul { + list-style-type: square; +} + +dd { + display: block; + margin-left: 40px; +} + +dl { + display: block; + margin-top: 1em; + margin-bottom: 1em; + margin-left: 0; + margin-right: 0; +} + +dt { + display: block; +} + +ol ul, ul ol, ul ul, ol ol { + margin-top: 0; + margin-bottom: 0 +} + +blockquote { + display: block; + margin-top: 1em; + margin-bottom: 1em; + margin-left: 40px; + margin-right: 40px; +} + +/*********** FORM ELEMENTS ************/ + +form { + display: block; + margin-top: 0em; +} + +option { + display: none; +} + +input, textarea, keygen, select, button, isindex { + margin: 0em; + color: initial; + line-height: normal; + text-transform: none; + text-indent: 0; + text-shadow: none; + display: inline-block; +} +input[type="hidden"] { + display: none; +} + + +article, aside, footer, header, hgroup, nav, section +{ + display: block; +} + +sub { + vertical-align: sub; + font-size: smaller; +} + +sup { + vertical-align: super; + font-size: smaller; +} + +)##"; } +#endif // LH_MASTER_CSS_H diff --git a/src/plugins/litehtml_viewer/litehtml/media_query.cpp b/src/plugins/litehtml_viewer/litehtml/media_query.cpp @@ -16,28 +16,28 @@ litehtml::media_query::media_query( const media_query& val ) m_media_type = val.m_media_type; } -litehtml::media_query::ptr litehtml::media_query::create_from_string(const tstring& str, const std::shared_ptr<document>& doc) +litehtml::media_query::ptr litehtml::media_query::create_from_string(const string& str, const std::shared_ptr<document>& doc) { media_query::ptr query = std::make_shared<media_query>(); string_vector tokens; - split_string(str, tokens, _t(" \t\r\n"), _t(""), _t("(")); + split_string(str, tokens, " \t\r\n", "", "("); - for(string_vector::iterator tok = tokens.begin(); tok != tokens.end(); tok++) + for(auto & token : tokens) { - if((*tok) == _t("not")) + if(token == "not") { query->m_not = true; - } else if(tok->at(0) == _t('(')) + } else if(token.at(0) == '(') { - tok->erase(0, 1); - if(tok->at(tok->length() - 1) == _t(')')) + token.erase(0, 1); + if(token.at(token.length() - 1) == ')') { - tok->erase(tok->length() - 1, 1); + token.erase(token.length() - 1, 1); } media_query_expression expr; string_vector expr_tokens; - split_string((*tok), expr_tokens, _t(":")); + split_string(token, expr_tokens, ":"); if(!expr_tokens.empty()) { trim(expr_tokens[0]); @@ -56,23 +56,20 @@ litehtml::media_query::ptr litehtml::media_query::create_from_string(const tstri expr.val = value_index(expr_tokens[1], media_orientation_strings, media_orientation_landscape); } else { - tstring::size_type slash_pos = expr_tokens[1].find(_t('/')); - if( slash_pos != tstring::npos ) + string::size_type slash_pos = expr_tokens[1].find('/'); + if( slash_pos != string::npos ) { - tstring val1 = expr_tokens[1].substr(0, slash_pos); - tstring val2 = expr_tokens[1].substr(slash_pos + 1); + string val1 = expr_tokens[1].substr(0, slash_pos); + string val2 = expr_tokens[1].substr(slash_pos + 1); trim(val1); trim(val2); - expr.val = t_atoi(val1.c_str()); - expr.val2 = t_atoi(val2.c_str()); + expr.val = atoi(val1.c_str()); + expr.val2 = atoi(val2.c_str()); } else { css_length length; length.fromString(expr_tokens[1]); - if(length.units() == css_units_dpcm) - { - expr.val = (int) (length.val() * 2.54); - } else if(length.units() == css_units_dpi) + if(length.units() == css_units_dpcm || length.units() == css_units_dpi) { expr.val = (int) (length.val() * 2.54); } else @@ -91,7 +88,7 @@ litehtml::media_query::ptr litehtml::media_query::create_from_string(const tstri } } else { - query->m_media_type = (media_type) value_index((*tok), media_type_strings, media_type_all); + query->m_media_type = (media_type) value_index(token, media_type_strings, media_type_all); } } @@ -105,11 +102,12 @@ bool litehtml::media_query::check( const media_features& features ) const if(m_media_type == media_type_all || m_media_type == features.type) { res = true; - for(media_query_expression::vector::const_iterator expr = m_expressions.begin(); expr != m_expressions.end() && res; expr++) + for(auto expression : m_expressions) { - if(!expr->check(features)) + if(!expression.check(features)) { res = false; + break; } } } @@ -124,19 +122,19 @@ bool litehtml::media_query::check( const media_features& features ) const ////////////////////////////////////////////////////////////////////////// -litehtml::media_query_list::ptr litehtml::media_query_list::create_from_string(const tstring& str, const std::shared_ptr<document>& doc) +litehtml::media_query_list::ptr litehtml::media_query_list::create_from_string(const string& str, const std::shared_ptr<document>& doc) { media_query_list::ptr list = std::make_shared<media_query_list>(); string_vector tokens; - split_string(str, tokens, _t(",")); + split_string(str, tokens, ","); - for(string_vector::iterator tok = tokens.begin(); tok != tokens.end(); tok++) + for(auto & token : tokens) { - trim(*tok); - lcase(*tok); + trim(token); + lcase(token); - litehtml::media_query::ptr query = media_query::create_from_string(*tok, doc); + litehtml::media_query::ptr query = media_query::create_from_string(token, doc); if(query) { list->m_queries.push_back(query); @@ -144,7 +142,7 @@ litehtml::media_query_list::ptr litehtml::media_query_list::create_from_string(c } if(list->m_queries.empty()) { - list = 0; + list = nullptr; } return list; @@ -154,11 +152,12 @@ bool litehtml::media_query_list::apply_media_features( const media_features& fea { bool apply = false; - for(media_query::vector::iterator iter = m_queries.begin(); iter != m_queries.end() && !apply; iter++) + for(auto & query : m_queries) { - if((*iter)->check(features)) + if(query->check(features)) { apply = true; + break; } } diff --git a/src/plugins/litehtml_viewer/litehtml/media_query.h b/src/plugins/litehtml_viewer/litehtml/media_query.h @@ -35,7 +35,7 @@ namespace litehtml media_query(); media_query(const media_query& val); - static media_query::ptr create_from_string(const tstring& str, const std::shared_ptr<document>& doc); + static media_query::ptr create_from_string(const string& str, const std::shared_ptr<document>& doc); bool check(const media_features& features) const; }; @@ -51,7 +51,7 @@ namespace litehtml media_query_list(); media_query_list(const media_query_list& val); - static media_query_list::ptr create_from_string(const tstring& str, const std::shared_ptr<document>& doc); + static media_query_list::ptr create_from_string(const string& str, const std::shared_ptr<document>& doc); bool is_used() const; bool apply_media_features(const media_features& features); // returns true if the m_is_used changed }; diff --git a/src/plugins/litehtml_viewer/litehtml/num_cvt.cpp b/src/plugins/litehtml_viewer/litehtml/num_cvt.cpp @@ -1,16 +1,15 @@ #include "num_cvt.h" -#include "types.h" #include "utf8_strings.h" #include <vector> -static std::vector<litehtml::tchar_t> latin_lower = { _t('a'), _t('b'), _t('c'), _t('d'), _t('e'), _t('f'), _t('g'), _t('h'), _t('i'), _t('j'), _t('k'), _t('l'), _t('m'), _t('n'), _t('o'), _t('p'), _t('q'), _t('r'), _t('s'), _t('t'), _t('u'), _t('v'), _t('w'), _t('x'), _t('y'), _t('z') }; -static std::vector<litehtml::tchar_t> latin_upper = { _t('A'), _t('B'), _t('C'), _t('D'), _t('E'), _t('F'), _t('G'), _t('H'), _t('I'), _t('J'), _t('K'), _t('L'), _t('M'), _t('N'), _t('O'), _t('P'), _t('Q'), _t('R'), _t('S'), _t('T'), _t('U'), _t('V'), _t('W'), _t('X'), _t('Y'), _t('Z') }; +static std::vector<char> latin_lower = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; +static std::vector<char> latin_upper = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; static std::vector<std::wstring> greek_lower = { L"α", L"β", L"γ", L"δ", L"ε", L"ζ", L"η", L"θ", L"ι", L"κ", L"λ", L"μ", L"ν", L"ξ", L"ο", L"π", L"ρ", L"σ", L"τ", L"υ", L"φ", L"χ", L"ψ", L"ω" }; -static litehtml::tstring to_mapped_alpha(int num, const std::vector<litehtml::tchar_t>& map) +static litehtml::string to_mapped_alpha(int num, const std::vector<char>& map) { int dividend = num; - litehtml::tstring out; + litehtml::string out; int modulo; while (dividend > 0) @@ -23,10 +22,10 @@ static litehtml::tstring to_mapped_alpha(int num, const std::vector<litehtml::tc return out; } -static litehtml::tstring to_mapped_alpha(int num, const std::vector<std::wstring>& map) +static litehtml::string to_mapped_alpha(int num, const std::vector<std::wstring>& map) { int dividend = num; - litehtml::tstring out; + litehtml::string out; int modulo; while (dividend > 0) @@ -39,37 +38,37 @@ static litehtml::tstring to_mapped_alpha(int num, const std::vector<std::wstring return out; } -litehtml::tstring litehtml::num_cvt::to_latin_lower(int val) +litehtml::string litehtml::num_cvt::to_latin_lower(int val) { return to_mapped_alpha(val, latin_lower); } -litehtml::tstring litehtml::num_cvt::to_latin_upper(int val) +litehtml::string litehtml::num_cvt::to_latin_upper(int val) { return to_mapped_alpha(val, latin_upper); } -litehtml::tstring litehtml::num_cvt::to_greek_lower(int val) +litehtml::string litehtml::num_cvt::to_greek_lower(int val) { return to_mapped_alpha(val, greek_lower); } -litehtml::tstring litehtml::num_cvt::to_roman_lower(int value) +litehtml::string litehtml::num_cvt::to_roman_lower(int value) { - struct romandata_t { int value; const litehtml::tchar_t* numeral; }; + struct romandata_t { int value; const char* numeral; }; const struct romandata_t romandata[] = { - { 1000, _t("m") }, { 900, _t("cm" )}, - { 500, _t("d") }, { 400, _t("cd") }, - { 100, _t("c") }, { 90, _t("xc") }, - { 50, _t("l") }, { 40, _t("xl") }, - { 10, _t("x") }, { 9, _t("ix") }, - { 5, _t("v") }, { 4, _t("iv") }, - { 1, _t("i") }, - { 0, NULL } // end marker + { 1000, "m" }, { 900, "cm" }, + { 500, "d" }, { 400, "cd" }, + { 100, "c" }, { 90, "xc" }, + { 50, "l" }, { 40, "xl" }, + { 10, "x" }, { 9, "ix" }, + { 5, "v" }, { 4, "iv" }, + { 1, "i" }, + { 0, nullptr } // end marker }; - litehtml::tstring result; + litehtml::string result; for (const romandata_t* current = romandata; current->value > 0; ++current) { while (value >= current->value) @@ -81,22 +80,22 @@ litehtml::tstring litehtml::num_cvt::to_roman_lower(int value) return result; } -litehtml::tstring litehtml::num_cvt::to_roman_upper(int value) +litehtml::string litehtml::num_cvt::to_roman_upper(int value) { - struct romandata_t { int value; const litehtml::tchar_t* numeral; }; + struct romandata_t { int value; const char* numeral; }; const struct romandata_t romandata[] = { - { 1000, _t("M") }, { 900, _t("CM") }, - { 500, _t("D") }, { 400, _t("CD") }, - { 100, _t("C") }, { 90, _t("XC") }, - { 50, _t("L") }, { 40, _t("XL") }, - { 10, _t("X") }, { 9, _t("IX") }, - { 5, _t("V") }, { 4, _t("IV") }, - { 1, _t("I") }, - { 0, NULL } // end marker + { 1000, "M" }, { 900, "CM" }, + { 500, "D" }, { 400, "CD" }, + { 100, "C" }, { 90, "XC" }, + { 50, "L" }, { 40, "XL" }, + { 10, "X" }, { 9, "IX" }, + { 5, "V" }, { 4, "IV" }, + { 1, "I" }, + { 0, nullptr } // end marker }; - litehtml::tstring result; + litehtml::string result; for (const romandata_t* current = romandata; current->value > 0; ++current) { while (value >= current->value) diff --git a/src/plugins/litehtml_viewer/litehtml/num_cvt.h b/src/plugins/litehtml_viewer/litehtml/num_cvt.h @@ -2,18 +2,17 @@ #define NUM_CVT_H #include <string> -#include <cstdint> #include "os_types.h" namespace litehtml { namespace num_cvt { - litehtml::tstring to_latin_lower(int val); - litehtml::tstring to_latin_upper(int val); - litehtml::tstring to_greek_lower(int val); - litehtml::tstring to_roman_lower(int value); - litehtml::tstring to_roman_upper(int value); + string to_latin_lower(int val); + string to_latin_upper(int val); + string to_greek_lower(int val); + string to_roman_lower(int value); + string to_roman_upper(int value); } } diff --git a/src/plugins/litehtml_viewer/litehtml/os_types.h b/src/plugins/litehtml_viewer/litehtml/os_types.h @@ -1,90 +1,28 @@ #ifndef LH_OS_TYPES_H #define LH_OS_TYPES_H +#include <string> +#include <cstdint> + namespace litehtml { -#if defined( WIN32 ) || defined( _WIN32 ) || defined( WINCE ) - -#ifndef LITEHTML_UTF8 - - typedef std::wstring tstring; - typedef wchar_t tchar_t; - typedef std::wstringstream tstringstream; - - #define _t(quote) L##quote - - #define t_strlen wcslen - #define t_strcmp wcscmp - #define t_strncmp wcsncmp - #define t_strcasecmp _wcsicmp - #define t_strncasecmp _wcsnicmp - #define t_strtol wcstol - #define t_atoi _wtoi - #define t_strtod wcstod - #define t_itoa(value, buffer, size, radix) _itow_s(value, buffer, size, radix) - #define t_strstr wcsstr - #define t_tolower towlower - #define t_isdigit iswdigit - #define t_to_string(val) std::to_wstring(val) - -#else - - typedef std::string tstring; - typedef char tchar_t; - typedef std::stringstream tstringstream; + using std::string; + typedef std::uintptr_t uint_ptr; - #define _t(quote) quote - - #define t_strlen strlen - #define t_strcmp strcmp - #define t_strncmp strncmp - #define t_strcasecmp _stricmp - #define t_strncasecmp _strnicmp - #define t_strtol strtol - #define t_atoi atoi - #define t_strtod strtod - #define t_itoa(value, buffer, size, radix) _itoa_s(value, buffer, size, radix) - #define t_strstr strstr - #define t_tolower tolower - #define t_isdigit isdigit - #define t_to_string(val) std::to_string(val) +#if defined( WIN32 ) || defined( _WIN32 ) || defined( WINCE ) +// noexcept appeared since Visual Studio 2015 +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define noexcept #endif -/* - #ifdef _WIN64 - typedef unsigned __int64 uint_ptr; - #else - typedef unsigned int uint_ptr; - #endif -*/ - typedef std::uintptr_t uint_ptr; + #define t_itoa(value, buffer, size, radix) _itoa_s(value, buffer, size, radix) + #define t_snprintf(s, n, format, ...) _snprintf_s(s, _TRUNCATE, n, format, __VA_ARGS__) #else - #define LITEHTML_UTF8 - typedef std::string tstring; - typedef char tchar_t; - typedef std::uintptr_t uint_ptr; - typedef std::stringstream tstringstream; - - #define _t(quote) quote - - #define t_strlen strlen - #define t_strcmp strcmp - #define t_strncmp strncmp - - #define t_strcasecmp strcasecmp - #define t_strncasecmp strncasecmp #define t_itoa(value, buffer, size, radix) snprintf(buffer, size, "%d", value) - - #define t_strtol strtol - #define t_atoi atoi - #define t_strtod strtod - #define t_strstr strstr - #define t_tolower tolower - #define t_isdigit isdigit - #define t_to_string(val) std::to_string(val) + #define t_snprintf(s, n, format, ...) snprintf(s, n, format, __VA_ARGS__) #endif } diff --git a/src/plugins/litehtml_viewer/litehtml/render_block.cpp b/src/plugins/litehtml_viewer/litehtml/render_block.cpp @@ -0,0 +1,856 @@ +#include "html.h" +#include "render_item.h" +#include "document.h" + +int litehtml::render_item_block::place_float(const std::shared_ptr<render_item> &el, int top, const containing_block_context &self_size) +{ + int line_top = get_cleared_top(el, top); + int line_left = 0; + int line_right = self_size.render_width; + get_line_left_right(line_top, self_size.render_width, line_left, line_right); + + int ret_width = 0; + + if (el->src_el()->css().get_float() == float_left) + { + el->render(line_left, line_top, self_size.new_width(line_right)); + if(el->right() > line_right) + { + int new_top = find_next_line_top(el->top(), el->width(), self_size.render_width); + el->pos().x = get_line_left(new_top) + el->content_offset_left(); + el->pos().y = new_top + el->content_offset_top(); + } + add_float(el, 0, 0, self_size.context_idx); + fix_line_width(float_left, self_size); + ret_width = el->right(); + } else if (el->src_el()->css().get_float() == float_right) + { + el->render(0, line_top, self_size.new_width(line_right)); + + if(line_left + el->width() > line_right) + { + int new_top = find_next_line_top(el->top(), el->width(), self_size.render_width); + el->pos().x = get_line_right(new_top, self_size.render_width) - el->width() + el->content_offset_left(); + el->pos().y = new_top + el->content_offset_top(); + } else + { + el->pos().x = line_right - el->width() + el->content_offset_left(); + } + add_float(el, 0, 0, self_size.context_idx); + fix_line_width(float_right, self_size); + line_left = 0; + line_right = self_size.render_width; + get_line_left_right(line_top, self_size.render_width, line_left, line_right); + + ret_width = ret_width + (self_size.render_width - line_right); + } + return ret_width; +} + +int litehtml::render_item_block::get_floats_height(element_float el_float) const +{ + if(src_el()->is_floats_holder()) + { + int h = 0; + + for(const auto& fb : m_floats_left) + { + bool process = false; + switch(el_float) + { + case float_none: + process = true; + break; + case float_left: + if (fb.clear_floats == clear_left || fb.clear_floats == clear_both) + { + process = true; + } + break; + case float_right: + if (fb.clear_floats == clear_right || fb.clear_floats == clear_both) + { + process = true; + } + break; + } + if(process) + { + if(el_float == float_none) + { + h = std::max(h, fb.pos.bottom()); + } else + { + h = std::max(h, fb.pos.top()); + } + } + } + + + for(const auto& fb : m_floats_right) + { + int process = false; + switch(el_float) + { + case float_none: + process = true; + break; + case float_left: + if (fb.clear_floats == clear_left || fb.clear_floats == clear_both) + { + process = true; + } + break; + case float_right: + if (fb.clear_floats == clear_right || fb.clear_floats == clear_both) + { + process = true; + } + break; + } + if(process) + { + if(el_float == float_none) + { + h = std::max(h, fb.pos.bottom()); + } else + { + h = std::max(h, fb.pos.top()); + } + } + } + + return h; + } + auto el_parent = parent(); + if (el_parent) + { + int h = el_parent->get_floats_height(el_float); + return h - m_pos.y; + } + return 0; +} + +int litehtml::render_item_block::get_left_floats_height() const +{ + if(src_el()->is_floats_holder()) + { + int h = 0; + if(!m_floats_left.empty()) + { + for (const auto& fb : m_floats_left) + { + h = std::max(h, fb.pos.bottom()); + } + } + return h; + } + auto el_parent = parent(); + if (el_parent) + { + int h = el_parent->get_left_floats_height(); + return h - m_pos.y; + } + return 0; +} + +int litehtml::render_item_block::get_right_floats_height() const +{ + if(src_el()->is_floats_holder()) + { + int h = 0; + if(!m_floats_right.empty()) + { + for(const auto& fb : m_floats_right) + { + h = std::max(h, fb.pos.bottom()); + } + } + return h; + } + auto el_parent = parent(); + if (el_parent) + { + int h = el_parent->get_right_floats_height(); + return h - m_pos.y; + } + return 0; +} + +int litehtml::render_item_block::get_line_left( int y ) +{ + if(src_el()->is_floats_holder()) + { + if(m_cache_line_left.is_valid && m_cache_line_left.hash == y) + { + return m_cache_line_left.val; + } + + int w = 0; + for(const auto& fb : m_floats_left) + { + if (y >= fb.pos.top() && y < fb.pos.bottom()) + { + w = std::max(w, fb.pos.right()); + if (w < fb.pos.right()) + { + break; + } + } + } + m_cache_line_left.set_value(y, w); + return w; + } + auto el_parent = parent(); + if (el_parent) + { + int w = el_parent->get_line_left(y + m_pos.y); + if (w < 0) + { + w = 0; + } + return w - (w ? m_pos.x : 0); + } + return 0; +} + +int litehtml::render_item_block::get_line_right( int y, int def_right ) +{ + if(src_el()->is_floats_holder()) + { + if(m_cache_line_right.is_valid && m_cache_line_right.hash == y) + { + if(m_cache_line_right.is_default) + { + return def_right; + } else + { + return std::min(m_cache_line_right.val, def_right); + } + } + + int w = def_right; + m_cache_line_right.is_default = true; + for(const auto& fb : m_floats_right) + { + if(y >= fb.pos.top() && y < fb.pos.bottom()) + { + w = std::min(w, fb.pos.left()); + m_cache_line_right.is_default = false; + if(w > fb.pos.left()) + { + break; + } + } + } + m_cache_line_right.set_value(y, w); + return w; + } + auto el_parent = parent(); + if (el_parent) + { + int w = el_parent->get_line_right(y + m_pos.y, def_right + m_pos.x); + return w - m_pos.x; + } + return 0; +} + + +void litehtml::render_item_block::get_line_left_right( int y, int def_right, int& ln_left, int& ln_right ) +{ + if(src_el()->is_floats_holder()) + { + ln_left = get_line_left(y); + ln_right = get_line_right(y, def_right); + } else + { + auto el_parent = parent(); + if (el_parent) + { + el_parent->get_line_left_right(y + m_pos.y, def_right + m_pos.x, ln_left, ln_right); + } + ln_right -= m_pos.x; + + if(ln_left < 0) + { + ln_left = 0; + } else if (ln_left > 0) + { + ln_left -= m_pos.x; + if (ln_left < 0) + { + ln_left = 0; + } + } + } +} + +void litehtml::render_item_block::clear_floats(int context) +{ + if(src_el()->is_floats_holder()) + { + auto iter = m_floats_left.begin(); + while(iter != m_floats_left.end()) + { + if(iter->context >= context) + { + iter = m_floats_left.erase(iter); + m_cache_line_left.invalidate(); + } else + { + iter++; + } + } + + iter = m_floats_right.begin(); + while(iter != m_floats_right.end()) + { + if(iter->context >= context) + { + iter = m_floats_right.erase(iter); + m_cache_line_right.invalidate(); + } else + { + iter++; + } + } + } else + { + auto el_parent = parent(); + if (el_parent) + { + el_parent->clear_floats(context); + } + } +} + +void litehtml::render_item_block::add_float(const std::shared_ptr<render_item> &el, int x, int y, int context) +{ + if(src_el()->is_floats_holder()) + { + floated_box fb; + fb.pos.x = el->left() + x; + fb.pos.y = el->top() + y; + fb.pos.width = el->width(); + fb.pos.height = el->height(); + fb.float_side = el->src_el()->css().get_float(); + fb.clear_floats = el->src_el()->css().get_clear(); + fb.el = el; + fb.context = context; + + if(fb.float_side == float_left) + { + if(m_floats_left.empty()) + { + m_floats_left.push_back(fb); + } else + { + bool inserted = false; + for(auto i = m_floats_left.begin(); i != m_floats_left.end(); i++) + { + if(fb.pos.right() > i->pos.right()) + { + m_floats_left.insert(i, std::move(fb)); + inserted = true; + break; + } + } + if(!inserted) + { + m_floats_left.push_back(std::move(fb)); + } + } + m_cache_line_left.invalidate(); + } else if(fb.float_side == float_right) + { + if(m_floats_right.empty()) + { + m_floats_right.push_back(std::move(fb)); + } else + { + bool inserted = false; + for(auto i = m_floats_right.begin(); i != m_floats_right.end(); i++) + { + if(fb.pos.left() < i->pos.left()) + { + m_floats_right.insert(i, std::move(fb)); + inserted = true; + break; + } + } + if(!inserted) + { + m_floats_right.push_back(fb); + } + } + m_cache_line_right.invalidate(); + } + } else + { + auto el_parent = parent(); + if (el_parent) + { + el_parent->add_float(el, x + m_pos.x, y + m_pos.y, context); + } + } +} + +int litehtml::render_item_block::get_cleared_top(const std::shared_ptr<render_item> &el, int line_top) const +{ + switch(el->src_el()->css().get_clear()) + { + case clear_left: + { + int fh = get_left_floats_height(); + if(fh && fh > line_top) + { + line_top = fh; + } + } + break; + case clear_right: + { + int fh = get_right_floats_height(); + if(fh && fh > line_top) + { + line_top = fh; + } + } + break; + case clear_both: + { + int fh = get_floats_height(float_none); + if(fh && fh > line_top) + { + line_top = fh; + } + } + break; + default: + if(el->src_el()->css().get_float() != float_none) + { + int fh = get_floats_height(el->src_el()->css().get_float()); + if(fh && fh > line_top) + { + line_top = fh; + } + } + break; + } + return line_top; +} + +int litehtml::render_item_block::find_next_line_top( int top, int width, int def_right ) +{ + if(src_el()->is_floats_holder()) + { + int new_top = top; + int_vector points; + + for(const auto& fb : m_floats_left) + { + if(fb.pos.top() >= top) + { + if(find(points.begin(), points.end(), fb.pos.top()) == points.end()) + { + points.push_back(fb.pos.top()); + } + } + if (fb.pos.bottom() >= top) + { + if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end()) + { + points.push_back(fb.pos.bottom()); + } + } + } + + for (const auto& fb : m_floats_right) + { + if (fb.pos.top() >= top) + { + if (find(points.begin(), points.end(), fb.pos.top()) == points.end()) + { + points.push_back(fb.pos.top()); + } + } + if (fb.pos.bottom() >= top) + { + if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end()) + { + points.push_back(fb.pos.bottom()); + } + } + } + + if(!points.empty()) + { + sort(points.begin(), points.end(), std::less<int>( )); + new_top = points.back(); + + for(auto pt : points) + { + int pos_left = 0; + int pos_right = def_right; + get_line_left_right(pt, def_right, pos_left, pos_right); + + if(pos_right - pos_left >= width) + { + new_top = pt; + break; + } + } + } + return new_top; + } + auto el_parent = parent(); + if (el_parent) + { + int new_top = el_parent->find_next_line_top(top + m_pos.y, width, def_right + m_pos.x); + return new_top - m_pos.y; + } + return 0; +} + +void litehtml::render_item_block::update_floats(int dy, const std::shared_ptr<render_item> &_parent) +{ + if(src_el()->is_floats_holder()) + { + bool reset_cache = false; + for(auto fb = m_floats_left.rbegin(); fb != m_floats_left.rend(); fb++) + { + if(fb->el->src_el()->is_ancestor(_parent->src_el())) + { + reset_cache = true; + fb->pos.y += dy; + } + } + if(reset_cache) + { + m_cache_line_left.invalidate(); + } + reset_cache = false; + for(auto fb = m_floats_right.rbegin(); fb != m_floats_right.rend(); fb++) + { + if(fb->el->src_el()->is_ancestor(_parent->src_el())) + { + reset_cache = true; + fb->pos.y += dy; + } + } + if(reset_cache) + { + m_cache_line_right.invalidate(); + } + } else + { + auto el_parent = parent(); + if (el_parent) + { + el_parent->update_floats(dy, _parent); + } + } +} + +std::shared_ptr<litehtml::render_item> litehtml::render_item_block::init() +{ + { + css_selector sel; + sel.parse(".inline_rating"); + if(src_el()->select(sel)) + { + int i = 0; + i++; + } + } + std::shared_ptr<render_item> ret; + + // Initialize indexes for list items + if(src_el()->css().get_display() == display_list_item && src_el()->css().get_list_style_type() >= list_style_type_armenian) + { + if (auto p = src_el()->parent()) + { + int val = atoi(p->get_attr("start", "1")); + for (int i = 0; i < (int)p->get_children_count(); i++) + { + auto child = p->get_child(i); + if (child == src_el()) + { + src_el()->set_attr("list_index", std::to_string(val).c_str()); + break; + } + else if (child->css().get_display() == display_list_item) + val++; + } + } + } + // Split inline blocks with box blocks inside + auto iter = m_children.begin(); + while (iter != m_children.end()) + { + const auto& el = *iter; + if(el->src_el()->css().get_display() == display_inline && !el->children().empty()) + { + auto split_el = el->split_inlines(); + if(std::get<0>(split_el)) + { + iter = m_children.erase(iter); + iter = m_children.insert(iter, std::get<2>(split_el)); + iter = m_children.insert(iter, std::get<1>(split_el)); + iter = m_children.insert(iter, std::get<0>(split_el)); + + std::get<0>(split_el)->parent(shared_from_this()); + std::get<1>(split_el)->parent(shared_from_this()); + std::get<2>(split_el)->parent(shared_from_this()); + continue; + } + } + ++iter; + } + + bool has_block_level = false; + bool has_inlines = false; + bool has_floats = false; + for (const auto& el : m_children) + { + if(!el->src_el()->is_float()) + { + if (el->src_el()->is_block_box()) + { + has_block_level = true; + } else if (el->src_el()->is_inline_box()) + { + has_inlines = true; + } + } + if(has_block_level && has_inlines) + break; + } + if(has_block_level) + { + ret = std::make_shared<render_item_block_context>(src_el()); + ret->parent(parent()); + + auto doc = src_el()->get_document(); + decltype(m_children) new_children; + decltype(m_children) inlines; + bool not_ws_added = false; + for (const auto& el : m_children) + { + if(el->src_el()->is_inline_box()) + { + inlines.push_back(el); + if(!el->src_el()->is_white_space()) + not_ws_added = true; + } else + { + if(not_ws_added) + { + auto anon_el = std::make_shared<html_tag>(src_el()); + auto anon_ri = std::make_shared<render_item_block>(anon_el); + for(const auto& inl : inlines) + { + anon_ri->add_child(inl); + } + + not_ws_added = false; + new_children.push_back(anon_ri); + anon_ri->parent(ret); + } + new_children.push_back(el); + el->parent(ret); + inlines.clear(); + } + } + if(!inlines.empty() && not_ws_added) + { + auto anon_el = std::make_shared<html_tag>(src_el()); + auto anon_ri = std::make_shared<render_item_block>(anon_el); + for(const auto& inl : inlines) + { + anon_ri->add_child(inl); + } + + new_children.push_back(anon_ri); + anon_ri->parent(ret); + } + ret->children() = new_children; + } + + if(!ret) + { + ret = std::make_shared<render_item_inline_context>(src_el()); + ret->parent(parent()); + ret->children() = children(); + for (const auto &el: ret->children()) + { + el->parent(ret); + } + } + + ret->src_el()->add_render(ret); + + for(auto& el : ret->children()) + { + el = el->init(); + } + + return ret; +} + +int litehtml::render_item_block::render(int x, int y, const containing_block_context &containing_block_size, bool second_pass) +{ + int ret_width = 0; + calc_outlines(containing_block_size.width); + + m_pos.clear(); + m_pos.move_to(x, y); + + m_pos.x += content_offset_left(); + m_pos.y += content_offset_top(); + + m_floats_left.clear(); + m_floats_right.clear(); + m_cache_line_left.invalidate(); + m_cache_line_right.invalidate(); + + containing_block_context self_size = calculate_containing_block_context(containing_block_size); + + //***************************************** + // Render content + //***************************************** + ret_width = _render_content(x, y, second_pass, ret_width, self_size); + //***************************************** + + 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) + { + 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; + } else + { + if(src_el()->is_inline_box() || + src_el()->css().get_float() != float_none || + src_el()->css().get_display() == display_table_cell || + src_el()->css().get_display() == display_table_caption || + src_el()->css().get_position() > element_position_relative) + { + m_pos.width = ret_width; + if(ret_width < self_size.render_width && !second_pass) + { + // We have to render content again with new max_width + requires_rerender = true; + } + } 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 = self_size.min_width; + requires_rerender = true; + } + } + + // Fix width with max-width attribute + if(self_size.max_width.type != containing_block_context::cbc_value_type_none) + { + if(m_pos.width > self_size.max_width) + { + m_pos.width = self_size.max_width; + requires_rerender = true; + } + } + + // re-render content with new width if required + if (requires_rerender && !second_pass && !is_root()) + { + if(src_el()->is_floats_holder()) + { + m_floats_left.clear(); + m_floats_right.clear(); + } else + { + clear_floats(self_size.context_idx); + } + m_cache_line_left.invalidate(); + m_cache_line_right.invalidate(); + + _render_content(x, y, true, ret_width, self_size.new_width(m_pos.width)); + } + + // Set block height + if (self_size.height.type != containing_block_context::cbc_value_type_auto) + { + m_pos.height = self_size.height; + if(src_el()->css().get_box_sizing() == box_sizing_border_box) + { + m_pos.height -= box_sizing_height(); + } + } else if (src_el()->is_floats_holder()) + { + // add the floats' height to the block height + int floats_height = get_floats_height(); + if (floats_height > m_pos.height) + { + m_pos.height = floats_height; + } + } + + // Fix height with min-height attribute + if(self_size.min_height.type != containing_block_context::cbc_value_type_none) + { + if(m_pos.height < self_size.min_height) + { + m_pos.height = self_size.min_height; + } + } + + // Fix width with max-width attribute + if(self_size.max_height.type != containing_block_context::cbc_value_type_none) + { + if(m_pos.height > self_size.max_height) + { + m_pos.height = self_size.max_height; + } + } + + // calculate the final position + m_pos.move_to(x, y); + m_pos.x += content_offset_left(); + m_pos.y += content_offset_top(); + + if (src_el()->css().get_display() == display_list_item) + { + string list_image = src_el()->css().get_list_style_image(); + if (!list_image.empty()) + { + size sz; + string list_image_baseurl = src_el()->css().get_list_style_image_baseurl(); + src_el()->get_document()->container()->get_image_size(list_image.c_str(), list_image_baseurl.c_str(), sz); + if (m_pos.height < sz.height) + { + m_pos.height = sz.height; + } + } + + } + + if (src_el()->is_floats_holder() && !second_pass) + { + for (const auto& fb : m_floats_left) + { + fb.el->apply_relative_shift(containing_block_size); + } + } + return ret_width + content_offset_width(); +} diff --git a/src/plugins/litehtml_viewer/litehtml/render_block_context.cpp b/src/plugins/litehtml_viewer/litehtml/render_block_context.cpp @@ -0,0 +1,125 @@ +#include "html.h" +#include "render_item.h" +#include "document.h" + +int litehtml::render_item_block_context::_render_content(int x, int y, bool second_pass, int ret_width, + const containing_block_context &self_size) +{ + element_position el_position; + + int child_top = 0; + int last_margin = 0; + std::shared_ptr<render_item> last_margin_el; + bool is_first = true; + for (const auto& el : m_children) + { + // we don't need to process absolute and fixed positioned element on the second pass + if (second_pass) + { + el_position = el->src_el()->css().get_position(); + if ((el_position == element_position_absolute || el_position == element_position_fixed)) continue; + } + + if(el->src_el()->css().get_float() != float_none) + { + int rw = place_float(el, child_top, self_size); + if (rw > ret_width) + { + ret_width = rw; + } + } else if(el->src_el()->css().get_display() != display_none) + { + if(el->src_el()->css().get_position() == element_position_absolute || el->src_el()->css().get_position() == element_position_fixed) + { + el->render(0, child_top, self_size); + } else + { + child_top = get_cleared_top(el, child_top); + int child_x = 0; + int child_width = self_size.render_width; + + el->calc_outlines(self_size.width); + + // 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; + } + } else + { + 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_floats_holder() || el->src_el()->css().get_display() == display_table) + { + int ln_left = 0; + int ln_right = child_width; + get_line_left_right(child_top, child_width, ln_left, ln_right); + child_x = ln_left; + child_width = ln_right - ln_left; + + auto el_parent = el->parent(); + el->pos().width = el->src_el()->css().get_width().calc_percent(child_width); + el->pos().height = el->src_el()->css().get_height().calc_percent(el_parent ? el_parent->pos().height : 0); + } + + int rw = el->render(child_x, child_top, self_size.new_width(child_width)); + // Render table with "width: auto" into returned width + if(el->src_el()->css().get_display() == display_table && rw < child_width && el->src_el()->css().get_width().is_predefined()) + { + el->render(child_x, child_top, self_size.new_width(rw)); + } + int auto_margin = el->calc_auto_margins(child_width); + if(auto_margin) + { + el->pos().x += auto_margin; + } + if (rw > ret_width) + { + ret_width = rw; + } + child_top += el->height(); + last_margin = el->get_margins().bottom; + last_margin_el = el; + is_first = false; + + if (el->src_el()->css().get_position() == element_position_relative) + { + el->apply_relative_shift(self_size); + } + } + } + } + + int block_height = 0; + if (get_predefined_height(block_height, self_size.height)) + { + m_pos.height = block_height; + } else + { + m_pos.height = child_top; + if(collapse_bottom_margin()) + { + m_pos.height -= last_margin; + if(m_margins.bottom < last_margin) + { + m_margins.bottom = last_margin; + } + if(last_margin_el) + { + last_margin_el->get_margins().bottom = 0; + } + } + } + + return ret_width; +} diff --git a/src/plugins/litehtml_viewer/litehtml/render_flex.cpp b/src/plugins/litehtml_viewer/litehtml/render_flex.cpp @@ -0,0 +1,91 @@ +#include "html.h" +#include "render_item.h" +#include "types.h" + +int litehtml::render_item_flex::_render_content(int x, int y, bool second_pass, int ret_width, + const containing_block_context &self_size) +{ + return 0; +} + +void litehtml::render_item_flex::draw_children(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex) +{ + +} + +std::shared_ptr<litehtml::render_item> litehtml::render_item_flex::init() +{ + auto doc = src_el()->get_document(); + decltype(m_children) new_children; + decltype(m_children) inlines; + + auto convert_inlines = [&]() + { + if(!inlines.empty()) + { + // Find last not space + auto not_space = std::find_if(inlines.rbegin(), inlines.rend(), [&](const std::shared_ptr<render_item>& el) + { + return !el->src_el()->is_space(); + }); + if(not_space != inlines.rend()) + { + // Erase all spaces at the end + inlines.erase((not_space.base()), inlines.end()); + } + + auto anon_el = std::make_shared<html_tag>(src_el()); + auto anon_ri = std::make_shared<render_item_block>(anon_el); + for(const auto& inl : inlines) + { + anon_ri->add_child(inl); + } + anon_ri->parent(shared_from_this()); + + new_children.push_back(anon_ri->init()); + inlines.clear(); + } + }; + + for (const auto& el : m_children) + { + if(el->src_el()->css().get_display() == display_inline_text) + { + if(!inlines.empty()) + { + inlines.push_back(el); + } else + { + if (!el->src_el()->is_white_space()) + { + inlines.push_back(el); + } + } + } else + { + convert_inlines(); + if(el->src_el()->is_block_box()) + { + // Add block boxes as is + el->parent(shared_from_this()); + new_children.push_back(el->init()); + } else + { + // Wrap inlines with anonymous block box + auto anon_el = std::make_shared<html_tag>(el->src_el()); + auto anon_ri = std::make_shared<render_item_block>(anon_el); + anon_ri->add_child(el->init()); + anon_ri->parent(shared_from_this()); + new_children.push_back(anon_ri->init()); + } + } + } + convert_inlines(); + children() = new_children; + for(const auto& el : children()) + { + m_flex_items.emplace_back(new flex_item(el)); + } + + return shared_from_this(); +} diff --git a/src/plugins/litehtml_viewer/litehtml/render_image.cpp b/src/plugins/litehtml_viewer/litehtml/render_image.cpp @@ -0,0 +1,148 @@ +#include "html.h" +#include "render_item.h" +#include "document.h" + +int +litehtml::render_item_image::render(int x, int y, const containing_block_context &containing_block_size, bool second_pass) +{ + int parent_width = containing_block_size.width; + + calc_outlines(parent_width); + + m_pos.move_to(x, y); + + document::ptr doc = src_el()->get_document(); + + litehtml::size sz; + src_el()->get_content_size(sz, containing_block_size.width); + + m_pos.width = sz.width; + m_pos.height = sz.height; + + src_el()->css_w().set_line_height(height()); + + if(src_el()->css().get_height().is_predefined() && src_el()->css().get_width().is_predefined()) + { + m_pos.height = sz.height; + m_pos.width = sz.width; + + // check for max-width + if(!src_el()->css().get_max_width().is_predefined()) + { + int max_width = doc->to_pixels(src_el()->css().get_max_width(), src_el()->css().get_font_size(), parent_width); + if(m_pos.width > max_width) + { + m_pos.width = max_width; + } + if(sz.width) + { + m_pos.height = (int) ((float) m_pos.width * (float) sz.height / (float)sz.width); + } else + { + m_pos.height = sz.height; + } + } + + // check for max-height + if(!src_el()->css().get_max_height().is_predefined()) + { + int max_height = calc_max_height(sz.height, containing_block_size.height); + if(m_pos.height > max_height) + { + m_pos.height = max_height; + } + if(sz.height) + { + m_pos.width = (int) ((float )m_pos.height * (float)sz.width / (float)sz.height); + } else + { + m_pos.width = sz.width; + } + } + } 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(); + } + + // check for max-height + if(!src_el()->css().get_max_height().is_predefined()) + { + int max_height = calc_max_height(sz.height, containing_block_size.height); + if(m_pos.height > max_height) + { + m_pos.height = max_height; + } + } + + if(sz.height) + { + m_pos.width = (int) ((float )m_pos.height * (float)sz.width / (float)sz.height); + } else + { + m_pos.width = sz.width; + } + } else if(src_el()->css().get_height().is_predefined() && !src_el()->css().get_width().is_predefined()) + { + m_pos.width = (int) src_el()->css().get_width().calc_percent(parent_width); + + // check for max-width + if(!src_el()->css().get_max_width().is_predefined()) + { + int max_width = doc->to_pixels(src_el()->css().get_max_width(), src_el()->css().get_font_size(), parent_width); + if(m_pos.width > max_width) + { + m_pos.width = max_width; + } + } + + if(sz.width) + { + m_pos.height = (int) ((float) m_pos.width * (float) sz.height / (float)sz.width); + } else + { + m_pos.height = sz.height; + } + } else + { + 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(); + } + + // check for max-height + if(!src_el()->css().get_max_height().is_predefined()) + { + int max_height = calc_max_height(sz.height, containing_block_size.height); + if(m_pos.height > max_height) + { + m_pos.height = max_height; + } + } + + // check for max-height + if(!src_el()->css().get_max_width().is_predefined()) + { + int max_width = doc->to_pixels(src_el()->css().get_max_width(), src_el()->css().get_font_size(), parent_width); + if(m_pos.width > max_width) + { + m_pos.width = max_width; + } + } + } + + m_pos.x += content_offset_left(); + m_pos.y += content_offset_top(); + + return m_pos.width + content_offset_left() + content_offset_right(); +} + +int litehtml::render_item_image::calc_max_height(int image_height, int containing_block_height) +{ + document::ptr doc = src_el()->get_document(); + return doc->to_pixels(src_el()->css().get_max_height(), src_el()->css().get_font_size(), + containing_block_height == 0 ? image_height : containing_block_height); +} diff --git a/src/plugins/litehtml_viewer/litehtml/render_inline.cpp b/src/plugins/litehtml_viewer/litehtml/render_inline.cpp @@ -0,0 +1,3 @@ +#include "html.h" +#include "render_item.h" + diff --git a/src/plugins/litehtml_viewer/litehtml/render_inline_context.cpp b/src/plugins/litehtml_viewer/litehtml/render_inline_context.cpp @@ -0,0 +1,403 @@ +#include "html.h" +#include "render_item.h" +#include "document.h" +#include "iterators.h" + +int litehtml::render_item_inline_context::_render_content(int x, int y, bool second_pass, int ret_width, + const containing_block_context &self_size) +{ + m_line_boxes.clear(); + m_max_line_width = 0; + + white_space ws = src_el()->css().get_white_space(); + bool skip_spaces = false; + if (ws == white_space_normal || + ws == white_space_nowrap || + ws == white_space_pre_line) + { + skip_spaces = true; + } + + bool was_space = false; + + go_inside_inline go_inside_inlines_selector; + inline_selector select_inlines; + elements_iterator inlines_iter(true, &go_inside_inlines_selector, &select_inlines); + + inlines_iter.process(shared_from_this(), [&](const std::shared_ptr<render_item>& el, iterator_item_type item_type) + { + switch (item_type) + { + case iterator_item_type_child: + { + // skip spaces to make rendering a bit faster + if (skip_spaces) + { + if (el->src_el()->is_white_space()) + { + if (was_space) + { + el->skip(true); + return; + } else + { + was_space = true; + } + } else + { + // skip all spaces after line break + was_space = el->src_el()->is_break(); + } + } + // place element into rendering flow + place_inline(std::unique_ptr<line_box_item>(new line_box_item(el)), self_size); + } + break; + + case iterator_item_type_start_parent: + { + el->clear_inline_boxes(); + place_inline(std::unique_ptr<lbi_start>(new lbi_start(el)), self_size); + } + break; + + case iterator_item_type_end_parent: + { + place_inline(std::unique_ptr<lbi_end>(new lbi_end(el)), self_size); + } + break; + } + }); + + finish_last_box(true, self_size); + + if (!m_line_boxes.empty()) + { + if (collapse_top_margin()) + { + int old_top = m_margins.top; + m_margins.top = std::max(m_line_boxes.front()->top_margin(), m_margins.top); + if (m_margins.top != old_top) + { + update_floats(m_margins.top - old_top, shared_from_this()); + } + } + if (collapse_bottom_margin()) + { + m_margins.bottom = std::max(m_line_boxes.back()->bottom_margin(), m_margins.bottom); + m_pos.height = m_line_boxes.back()->bottom() - m_line_boxes.back()->bottom_margin(); + } + else + { + m_pos.height = m_line_boxes.back()->bottom(); + } + } + + return std::max(ret_width, m_max_line_width); +} + +void litehtml::render_item_inline_context::fix_line_width(element_float flt, + const containing_block_context &self_size) +{ + int ret_width = 0; + if(!m_line_boxes.empty()) + { + auto el_front = m_line_boxes.back()->get_first_text_part(); + + std::vector<std::shared_ptr<render_item>> els; + bool was_cleared = false; + if(el_front && el_front->src_el()->css().get_clear() != clear_none) + { + if(el_front->src_el()->css().get_clear() == clear_both) + { + was_cleared = true; + } else + { + if( (flt == float_left && el_front->src_el()->css().get_clear() == clear_left) || + (flt == float_right && el_front->src_el()->css().get_clear() == clear_right) ) + { + was_cleared = true; + } + } + } + + if(!was_cleared) + { + std::list<std::unique_ptr<line_box_item> > items = std::move(m_line_boxes.back()->items()); + m_line_boxes.pop_back(); + + for(auto& item : items) + { + place_inline(std::move(item), self_size); + } + } else + { + int line_top = 0; + line_top = m_line_boxes.back()->top(); + + int line_left = 0; + int line_right = self_size.render_width; + get_line_left_right(line_top, self_size.render_width, line_left, line_right); + + if(m_line_boxes.size() == 1) + { + if (src_el()->css().get_list_style_type() != list_style_type_none && src_el()->css().get_list_style_position() == list_style_position_inside) + { + int sz_font = src_el()->css().get_font_size(); + line_left += sz_font; + } + + if (src_el()->css().get_text_indent().val() != 0) + { + line_left += src_el()->css().get_text_indent().calc_percent(self_size.width); + } + + } + + auto items = m_line_boxes.back()->new_width(line_left, line_right); + for(auto& item : items) + { + place_inline(std::move(item), self_size); + } + } + } +} + +std::list<std::unique_ptr<litehtml::line_box_item> > litehtml::render_item_inline_context::finish_last_box(bool end_of_render, const containing_block_context &self_size) +{ + std::list<std::unique_ptr<line_box_item> > ret; + + if(!m_line_boxes.empty()) + { + ret = m_line_boxes.back()->finish(end_of_render, self_size); + + if(m_line_boxes.back()->is_empty() && end_of_render) + { + // remove the last empty line + m_line_boxes.pop_back(); + } else + { + m_max_line_width = std::max(m_max_line_width, m_line_boxes.back()->min_width()); + } + } + return ret; +} + +int litehtml::render_item_inline_context::new_box(const std::unique_ptr<line_box_item>& el, line_context& line_ctx, const containing_block_context &self_size) +{ + auto items = finish_last_box(false, self_size); + int line_top = 0; + if(!m_line_boxes.empty()) + { + line_top = m_line_boxes.back()->bottom(); + } + line_ctx.top = get_cleared_top(el->get_el(), line_top); + + line_ctx.left = 0; + line_ctx.right = self_size.render_width; + line_ctx.fix_top(); + get_line_left_right(line_ctx.top, self_size.render_width, line_ctx.left, line_ctx.right); + + if(el->get_el()->src_el()->is_inline_box() || el->get_el()->src_el()->is_floats_holder()) + { + if (el->get_el()->width() > line_ctx.right - line_ctx.left) + { + line_ctx.top = find_next_line_top(line_ctx.top, el->get_el()->width(), self_size.render_width); + line_ctx.left = 0; + line_ctx.right = self_size.render_width; + line_ctx.fix_top(); + get_line_left_right(line_ctx.top, self_size.render_width, line_ctx.left, line_ctx.right); + } + } + + int first_line_margin = 0; + int text_indent = 0; + if(m_line_boxes.empty()) + { + if(src_el()->css().get_list_style_type() != list_style_type_none && src_el()->css().get_list_style_position() == list_style_position_inside) + { + int sz_font = src_el()->css().get_font_size(); + first_line_margin = sz_font; + } + if(src_el()->css().get_text_indent().val() != 0) + { + text_indent = src_el()->css().get_text_indent().calc_percent(self_size.width); + } + } + + m_line_boxes.emplace_back(std::unique_ptr<line_box>(new line_box( + line_ctx.top, + line_ctx.left + first_line_margin + text_indent, line_ctx.right, + css().get_line_height(), + css().get_font_metrics(), + css().get_text_align()))); + + // Add items returned by finish_last_box function into the new line + for(auto& it : items) + { + m_line_boxes.back()->add_item(std::move(it)); + } + + return line_ctx.top; +} + +void litehtml::render_item_inline_context::place_inline(std::unique_ptr<line_box_item> item, const containing_block_context &self_size) +{ + if(item->get_el()->src_el()->css().get_display() == display_none) return; + + if(item->get_el()->src_el()->is_float()) + { + int line_top = 0; + if(!m_line_boxes.empty()) + { + line_top = m_line_boxes.back()->top(); + } + int ret = place_float(item->get_el(), line_top, self_size); + if(ret > m_max_line_width) + { + m_max_line_width = ret; + } + return; + } + + line_context line_ctx = {0}; + line_ctx.top = 0; + if (!m_line_boxes.empty()) + { + line_ctx.top = m_line_boxes.back().get()->top(); + } + line_ctx.left = 0; + line_ctx.right = self_size.render_width; + line_ctx.fix_top(); + get_line_left_right(line_ctx.top, self_size.render_width, line_ctx.left, line_ctx.right); + + if(item->get_type() == line_box_item::type_text_part) + { + switch (item->get_el()->src_el()->css().get_display()) + { + case display_inline_block: + case display_inline_table: + item->set_rendered_min_width(item->get_el()->render(line_ctx.left, line_ctx.top, self_size.new_width(line_ctx.right))); + break; + case display_inline_text: + { + litehtml::size sz; + item->get_el()->src_el()->get_content_size(sz, line_ctx.right); + item->get_el()->pos() = sz; + item->set_rendered_min_width(sz.width); + } + break; + } + } + + bool add_box = true; + if(!m_line_boxes.empty()) + { + if(m_line_boxes.back()->can_hold(item, src_el()->css().get_white_space())) + { + add_box = false; + } + } + if(add_box) + { + new_box(item, line_ctx, self_size); + } else if(!m_line_boxes.empty()) + { + line_ctx.top = m_line_boxes.back()->top(); + } + + if (line_ctx.top != line_ctx.calculatedTop) + { + line_ctx.left = 0; + line_ctx.right = self_size.render_width; + line_ctx.fix_top(); + get_line_left_right(line_ctx.top, self_size.render_width, line_ctx.left, line_ctx.right); + } + + if(!item->get_el()->src_el()->is_inline_box()) + { + if(m_line_boxes.size() == 1) + { + if(collapse_top_margin()) + { + int shift = item->get_el()->margin_top(); + if(shift >= 0) + { + line_ctx.top -= shift; + m_line_boxes.back()->y_shift(-shift); + } + } + } else + { + int shift = 0; + int prev_margin = m_line_boxes[m_line_boxes.size() - 2]->bottom_margin(); + + if(prev_margin > item->get_el()->margin_top()) + { + shift = item->get_el()->margin_top(); + } else + { + shift = prev_margin; + } + if(shift >= 0) + { + line_ctx.top -= shift; + m_line_boxes.back()->y_shift(-shift); + } + } + } + + m_line_boxes.back()->add_item(std::move(item)); +} + +void litehtml::render_item_inline_context::apply_vertical_align() +{ + if(!m_line_boxes.empty()) + { + int add = 0; + int content_height = m_line_boxes.back()->bottom(); + + if(m_pos.height > content_height) + { + switch(src_el()->css().get_vertical_align()) + { + case va_middle: + add = (m_pos.height - content_height) / 2; + break; + case va_bottom: + add = m_pos.height - content_height; + break; + default: + add = 0; + break; + } + } + + if(add) + { + for(auto & box : m_line_boxes) + { + box->y_shift(add); + } + } + } +} + +int litehtml::render_item_inline_context::get_base_line() +{ + 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; +} diff --git a/src/plugins/litehtml_viewer/litehtml/render_item.cpp b/src/plugins/litehtml_viewer/litehtml/render_item.cpp @@ -0,0 +1,1076 @@ +#include "html.h" +#include "render_item.h" +#include "document.h" +#include <typeinfo> +#include <utf8_strings.h> + +litehtml::render_item::render_item(std::shared_ptr<element> _src_el) : + m_element(std::move(_src_el)), + m_skip(false) +{ + document::ptr doc = src_el()->get_document(); + auto fnt_size = src_el()->css().get_font_size(); + + m_margins.left = doc->to_pixels(src_el()->css().get_margins().left, fnt_size); + m_margins.right = doc->to_pixels(src_el()->css().get_margins().right, fnt_size); + m_margins.top = doc->to_pixels(src_el()->css().get_margins().top, fnt_size); + m_margins.bottom = doc->to_pixels(src_el()->css().get_margins().bottom, fnt_size); + + m_padding.left = doc->to_pixels(src_el()->css().get_padding().left, fnt_size); + m_padding.right = doc->to_pixels(src_el()->css().get_padding().right, fnt_size); + m_padding.top = doc->to_pixels(src_el()->css().get_padding().top, fnt_size); + m_padding.bottom = doc->to_pixels(src_el()->css().get_padding().bottom, fnt_size); + + m_borders.left = doc->to_pixels(src_el()->css().get_borders().left.width, fnt_size); + m_borders.right = doc->to_pixels(src_el()->css().get_borders().right.width, fnt_size); + m_borders.top = doc->to_pixels(src_el()->css().get_borders().top.width, fnt_size); + m_borders.bottom = doc->to_pixels(src_el()->css().get_borders().bottom.width, fnt_size); +} + + +void litehtml::render_item::calc_outlines( int parent_width ) +{ + m_padding.left = m_element->css().get_padding().left.calc_percent(parent_width); + m_padding.right = m_element->css().get_padding().right.calc_percent(parent_width); + + m_borders.left = m_element->css().get_borders().left.width.calc_percent(parent_width); + m_borders.right = m_element->css().get_borders().right.width.calc_percent(parent_width); + + m_margins.left = m_element->css().get_margins().left.calc_percent(parent_width); + m_margins.right = m_element->css().get_margins().right.calc_percent(parent_width); + + m_margins.top = m_element->css().get_margins().top.calc_percent(parent_width); + m_margins.bottom = m_element->css().get_margins().bottom.calc_percent(parent_width); + + m_padding.top = m_element->css().get_padding().top.calc_percent(parent_width); + m_padding.bottom = m_element->css().get_padding().bottom.calc_percent(parent_width); +} + +int litehtml::render_item::calc_auto_margins(int parent_width) +{ + if ((src_el()->css().get_display() == display_block || src_el()->css().get_display() == display_table) && + src_el()->css().get_position() != element_position_absolute && + src_el()->css().get_float() == float_none) + { + if (src_el()->css().get_margins().left.is_predefined() && src_el()->css().get_margins().right.is_predefined()) + { + int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right; + if (el_width <= parent_width) + { + m_margins.left = (parent_width - el_width) / 2; + m_margins.right = (parent_width - el_width) - m_margins.left; + } + else + { + m_margins.left = 0; + m_margins.right = 0; + } + return m_margins.left; + } + else if (src_el()->css().get_margins().left.is_predefined() && !src_el()->css().get_margins().right.is_predefined()) + { + int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.right; + m_margins.left = parent_width - el_width; + if (m_margins.left < 0) m_margins.left = 0; + return m_margins.left; + } + else if (!src_el()->css().get_margins().left.is_predefined() && src_el()->css().get_margins().right.is_predefined()) + { + int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.left; + m_margins.right = parent_width - el_width; + if (m_margins.right < 0) m_margins.right = 0; + } + } + return 0; +} + +void litehtml::render_item::apply_relative_shift(const containing_block_context &containing_block_size) +{ + if (src_el()->css().get_position() == element_position_relative) + { + css_offsets offsets = src_el()->css().get_offsets(); + if (!offsets.left.is_predefined()) + { + m_pos.x += offsets.left.calc_percent(containing_block_size.width); + } + else if (!offsets.right.is_predefined()) + { + m_pos.x -= offsets.right.calc_percent(containing_block_size.width); + } + if (!offsets.top.is_predefined()) + { + m_pos.y += offsets.top.calc_percent(containing_block_size.height); + } + else if (!offsets.bottom.is_predefined()) + { + m_pos.y -= offsets.bottom.calc_percent(containing_block_size.height); + } + } +} + +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 true; +} + +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>, + std::shared_ptr<litehtml::render_item> + > litehtml::render_item::split_inlines() +{ + std::tuple< + std::shared_ptr<litehtml::render_item>, + std::shared_ptr<litehtml::render_item>, + std::shared_ptr<litehtml::render_item> + > ret; + for(const auto& child: m_children) + { + if(child->src_el()->is_block_box() && child->src_el()->css().get_float() == float_none) + { + std::get<0>(ret) = clone(); + std::get<1>(ret) = child; + std::get<2>(ret) = clone(); + + std::get<1>(ret)->parent(std::get<0>(ret)); + std::get<2>(ret)->parent(std::get<0>(ret)); + + bool found = false; + for(const auto& ch: m_children) + { + if(ch == child) + { + found = true; + continue; + } + if(!found) + { + std::get<0>(ret)->add_child(ch); + } else + { + std::get<2>(ret)->add_child(ch); + } + } + break; + } + if(!child->children().empty()) + { + auto child_split = child->split_inlines(); + if(std::get<0>(child_split)) + { + std::get<0>(ret) = clone(); + std::get<1>(ret) = std::get<1>(child_split); + std::get<2>(ret) = clone(); + + std::get<2>(ret)->parent(std::get<0>(ret)); + + bool found = false; + for(const auto& ch: m_children) + { + if(ch == child) + { + found = true; + continue; + } + if(!found) + { + std::get<0>(ret)->add_child(ch); + } else + { + std::get<2>(ret)->add_child(ch); + } + } + std::get<0>(ret)->add_child(std::get<0>(child_split)); + std::get<2>(ret)->add_child(std::get<2>(child_split)); + break; + } + } + } + return ret; +} + +bool litehtml::render_item::fetch_positioned() +{ + bool ret = false; + + m_positioned.clear(); + + litehtml::element_position el_pos; + + for(auto& el : m_children) + { + el_pos = el->src_el()->css().get_position(); + if (el_pos != element_position_static) + { + add_positioned(el); + } + if (!ret && (el_pos == element_position_absolute || el_pos == element_position_fixed)) + { + ret = true; + } + if(el->fetch_positioned()) + { + ret = true; + } + } + return ret; +} + +void litehtml::render_item::render_positioned(render_type rt) +{ + position wnd_position; + src_el()->get_document()->container()->get_client_rect(wnd_position); + + element_position el_position; + bool process; + for (auto& el : m_positioned) + { + el_position = el->src_el()->css().get_position(); + + process = false; + if(el->src_el()->css().get_display() != display_none) + { + if(el_position == element_position_absolute) + { + if(rt != render_fixed_only) + { + process = true; + } + } else if(el_position == element_position_fixed) + { + if(rt != render_no_fixed) + { + process = true; + } + } + } + + if(process) + { + containing_block_context containing_block_size; + int client_x = 0; + int client_y = 0; + if(el_position == element_position_fixed) + { + containing_block_size.height = wnd_position.height; + containing_block_size.width = wnd_position.width; + client_x = wnd_position.left(); + client_y = wnd_position.top(); + } else + { + containing_block_size.height = m_pos.height; + containing_block_size.width = m_pos.width; + } + + css_length css_left = el->src_el()->css().get_offsets().left; + css_length css_right = el->src_el()->css().get_offsets().right; + css_length css_top = el->src_el()->css().get_offsets().top; + css_length css_bottom = el->src_el()->css().get_offsets().bottom; + + bool need_render = false; + + css_length el_w = el->src_el()->css().get_width(); + css_length el_h = el->src_el()->css().get_height(); + + int new_width = -1; + int new_height = -1; + if(el_w.units() == css_units_percentage && containing_block_size.width) + { + new_width = el_w.calc_percent(containing_block_size.width); + if(el->m_pos.width != new_width) + { + need_render = true; + el->m_pos.width = new_width; + } + } + + if(el_h.units() == css_units_percentage && containing_block_size.height) + { + new_height = el_h.calc_percent(containing_block_size.height); + if(el->m_pos.height != new_height) + { + need_render = true; + el->m_pos.height = new_height; + } + } + + bool cvt_x = false; + bool cvt_y = false; + + if(el_position == element_position_fixed) + { + if(!css_left.is_predefined() || !css_right.is_predefined()) + { + if(!css_left.is_predefined() && css_right.is_predefined()) + { + el->m_pos.x = css_left.calc_percent(containing_block_size.width) + el->content_offset_left(); + } else if(css_left.is_predefined() && !css_right.is_predefined()) + { + el->m_pos.x = containing_block_size.width - css_right.calc_percent(containing_block_size.width) - el->m_pos.width - + el->content_offset_right(); + } else + { + el->m_pos.x = css_left.calc_percent(containing_block_size.width) + el->content_offset_left(); + el->m_pos.width = containing_block_size.width - + css_left.calc_percent(containing_block_size.width) - + css_right.calc_percent(containing_block_size.width) - + (el->content_offset_left() + el->content_offset_right()); + need_render = true; + } + } + + if(!css_top.is_predefined() || !css_bottom.is_predefined()) + { + if(!css_top.is_predefined() && css_bottom.is_predefined()) + { + el->m_pos.y = css_top.calc_percent(containing_block_size.height) + el->content_offset_top(); + } else if(css_top.is_predefined() && !css_bottom.is_predefined()) + { + el->m_pos.y = containing_block_size.height - css_bottom.calc_percent(containing_block_size.height) - el->m_pos.height - + el->content_offset_bottom(); + } else + { + el->m_pos.y = css_top.calc_percent(containing_block_size.height) + el->content_offset_top(); + el->m_pos.height = containing_block_size.height - + css_top.calc_percent(containing_block_size.height) - + css_bottom.calc_percent(containing_block_size.height) - + (el->content_offset_top() + el->content_offset_bottom()); + need_render = true; + } + } + } else + { + if(!css_left.is_predefined() || !css_right.is_predefined()) + { + if(!css_left.is_predefined() && css_right.is_predefined()) + { + el->m_pos.x = css_left.calc_percent(containing_block_size.height) + el->content_offset_left() - m_padding.left; + } else if(css_left.is_predefined() && !css_right.is_predefined()) + { + el->m_pos.x = m_pos.width + m_padding.right - css_right.calc_percent(containing_block_size.height) - el->m_pos.width - + el->content_offset_right(); + } else + { + el->m_pos.x = css_left.calc_percent(containing_block_size.height) + el->content_offset_left() - m_padding.left; + el->m_pos.width = m_pos.width + m_padding.left + m_padding.right - + css_left.calc_percent(containing_block_size.height) - + css_right.calc_percent(containing_block_size.height) - + (el->content_offset_left() + el->content_offset_right()); + if (new_width != -1) + { + el->m_pos.x += (el->m_pos.width - new_width) / 2; + el->m_pos.width = new_width; + } + need_render = true; + } + cvt_x = true; + } + + if(!css_top.is_predefined() || !css_bottom.is_predefined()) + { + if(!css_top.is_predefined() && css_bottom.is_predefined()) + { + el->m_pos.y = css_top.calc_percent(containing_block_size.height) + el->content_offset_top() - m_padding.top; + } else if(css_top.is_predefined() && !css_bottom.is_predefined()) + { + el->m_pos.y = m_pos.height + m_padding.bottom - css_bottom.calc_percent(containing_block_size.height) - el->m_pos.height - + el->content_offset_bottom(); + } else + { + el->m_pos.y = css_top.calc_percent(containing_block_size.height) + el->content_offset_top() - m_padding.top; + el->m_pos.height = m_pos.height + m_padding.top + m_padding.bottom - + css_top.calc_percent(containing_block_size.height) - + css_bottom.calc_percent(containing_block_size.height) - + (el->content_offset_top() + el->content_offset_bottom()); + if (new_height != -1) + { + el->m_pos.y += (el->m_pos.height - new_height) / 2; + el->m_pos.height = new_height; + } + need_render = true; + } + cvt_y = true; + } + } + + if(cvt_x || cvt_y) + { + int offset_x = 0; + int offset_y = 0; + auto cur_el = el->parent(); + auto this_el = shared_from_this(); + while(cur_el && cur_el != this_el) + { + offset_x += cur_el->m_pos.x; + offset_y += cur_el->m_pos.y; + cur_el = cur_el->parent(); + } + if(cvt_x) el->m_pos.x -= offset_x; + if(cvt_y) el->m_pos.y -= offset_y; + } + + if(need_render) + { + position pos = el->m_pos; + el->render(el->left(), el->top(), containing_block_size.new_width(el->width()), true); + el->m_pos = pos; + } + + if(el_position == element_position_fixed) + { + position fixed_pos; + el->get_redraw_box(fixed_pos); + src_el()->get_document()->add_fixed_box(fixed_pos); + } + } + + el->render_positioned(); + } + + if(!m_positioned.empty()) + { + std::stable_sort(m_positioned.begin(), m_positioned.end(), [](const std::shared_ptr<render_item>& Left, const std::shared_ptr<render_item>& Right) + { + return (Left->src_el()->css().get_z_index() < Right->src_el()->css().get_z_index()); + }); + } +} + +void litehtml::render_item::add_positioned(const std::shared_ptr<litehtml::render_item> &el) +{ + if (src_el()->css().get_position() != element_position_static || is_root()) + { + m_positioned.push_back(el); + } else + { + auto el_parent = parent(); + if (el_parent) + { + el_parent->add_positioned(el); + } + } +} + +void litehtml::render_item::get_redraw_box(litehtml::position& pos, int x /*= 0*/, int y /*= 0*/) +{ + if(is_visible()) + { + int p_left = std::min(pos.left(), x + m_pos.left() - m_padding.left - m_borders.left); + int p_right = std::max(pos.right(), x + m_pos.right() + m_padding.left + m_borders.left); + int p_top = std::min(pos.top(), y + m_pos.top() - m_padding.top - m_borders.top); + int p_bottom = std::max(pos.bottom(), y + m_pos.bottom() + m_padding.bottom + m_borders.bottom); + + pos.x = p_left; + pos.y = p_top; + pos.width = p_right - p_left; + pos.height = p_bottom - p_top; + + if(src_el()->css().get_overflow() == overflow_visible) + { + for(auto& el : m_children) + { + if(el->src_el()->css().get_position() != element_position_fixed) + { + el->get_redraw_box(pos, x + m_pos.x, y + m_pos.y); + } + } + } + } +} + +void litehtml::render_item::calc_document_size( litehtml::size& sz, litehtml::size& content_size, int x /*= 0*/, int y /*= 0*/ ) +{ + if(is_visible() && src_el()->css().get_position() != element_position_fixed) + { + sz.width = std::max(sz.width, x + right()); + sz.height = std::max(sz.height, y + bottom()); + + if(!src_el()->is_root() && !src_el()->is_body()) + { + content_size.width = std::max(content_size.width, x + right()); + content_size.height = std::max(content_size.height, y + bottom()); + } + + // All children of tables and blocks with style other than "overflow: visible" are inside element. + // We can skip calculating size of children + if(src_el()->css().get_overflow() == overflow_visible && src_el()->css().get_display() != display_table) + { + for(auto& el : m_children) + { + el->calc_document_size(sz, content_size, x + m_pos.x, y + m_pos.y); + } + } + + if(src_el()->is_root() || src_el()->is_body()) + { + content_size.width += content_offset_right(); + content_size.height += content_offset_bottom(); + } + } +} + +void litehtml::render_item::draw_stacking_context( uint_ptr hdc, int x, int y, const position* clip, bool with_positioned ) +{ + if(!is_visible()) return; + + std::map<int, bool> z_indexes; + if(with_positioned) + { + for(const auto& idx : m_positioned) + { + z_indexes[idx->src_el()->css().get_z_index()]; + } + + for(const auto& idx : z_indexes) + { + if(idx.first < 0) + { + draw_children(hdc, x, y, clip, draw_positioned, idx.first); + } + } + } + draw_children(hdc, x, y, clip, draw_block, 0); + draw_children(hdc, x, y, clip, draw_floats, 0); + draw_children(hdc, x, y, clip, draw_inlines, 0); + if(with_positioned) + { + for(auto& z_index : z_indexes) + { + if(z_index.first == 0) + { + draw_children(hdc, x, y, clip, draw_positioned, z_index.first); + } + } + + for(auto& z_index : z_indexes) + { + if(z_index.first > 0) + { + draw_children(hdc, x, y, clip, draw_positioned, z_index.first); + } + } + } +} + +void litehtml::render_item::draw_children(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex) +{ + position pos = m_pos; + pos.x += x; + pos.y += y; + + document::ptr doc = src_el()->get_document(); + + if (src_el()->css().get_overflow() > overflow_visible) + { + // TODO: Process overflow for inline elements + if(src_el()->css().get_display() != display_inline) + { + position border_box = pos; + border_box += m_padding; + border_box += m_borders; + + border_radiuses bdr_radius = src_el()->css().get_borders().radius.calc_percents(border_box.width, + border_box.height); + + bdr_radius -= m_borders; + bdr_radius -= m_padding; + + doc->container()->set_clip(pos, bdr_radius); + } + } + + for (const auto& el : m_children) + { + if (el->is_visible()) + { + bool process = true; + switch (flag) + { + case draw_positioned: + if (el->src_el()->is_positioned() && el->src_el()->css().get_z_index() == zindex) + { + if (el->src_el()->css().get_position() == element_position_fixed) + { + position browser_wnd; + doc->container()->get_client_rect(browser_wnd); + + el->src_el()->draw(hdc, browser_wnd.x, browser_wnd.y, clip, el); + el->draw_stacking_context(hdc, browser_wnd.x, browser_wnd.y, clip, true); + } + else + { + el->src_el()->draw(hdc, pos.x, pos.y, clip, el); + el->draw_stacking_context(hdc, pos.x, pos.y, clip, true); + } + process = false; + } + break; + case draw_block: + if (!el->src_el()->is_inline_box() && el->src_el()->css().get_float() == float_none && !el->src_el()->is_positioned()) + { + el->src_el()->draw(hdc, pos.x, pos.y, clip, el); + } + break; + case draw_floats: + if (el->src_el()->css().get_float() != float_none && !el->src_el()->is_positioned()) + { + el->src_el()->draw(hdc, pos.x, pos.y, clip, el); + el->draw_stacking_context(hdc, pos.x, pos.y, clip, false); + process = false; + } + break; + case draw_inlines: + if (el->src_el()->is_inline_box() && 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) + { + el->draw_stacking_context(hdc, pos.x, pos.y, clip, false); + process = false; + } + } + break; + default: + break; + } + + if (process) + { + if (flag == draw_positioned) + { + if (!el->src_el()->is_positioned()) + { + el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex); + } + } + else + { + if (el->src_el()->css().get_float() == float_none && + el->src_el()->css().get_display() != display_inline_block && + !el->src_el()->is_positioned()) + { + el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex); + } + } + } + } + } + + if (src_el()->css().get_overflow() > overflow_visible) + { + doc->container()->del_clip(); + } +} + +std::shared_ptr<litehtml::element> litehtml::render_item::get_child_by_point(int x, int y, int client_x, int client_y, draw_flag flag, int zindex) +{ + element::ptr ret = nullptr; + + if(src_el()->css().get_overflow() > overflow_visible) + { + if(!m_pos.is_point_inside(x, y)) + { + return ret; + } + } + + position el_pos = m_pos; + el_pos.x = x - el_pos.x; + el_pos.y = y - el_pos.y; + + for(auto i = m_children.rbegin(); i != m_children.rend() && !ret; std::advance(i, 1)) + { + auto el = (*i); + + if(el->is_visible() && el->src_el()->css().get_display() != display_inline_text) + { + switch(flag) + { + case draw_positioned: + if(el->src_el()->is_positioned() && el->src_el()->css().get_z_index() == zindex) + { + if(el->src_el()->css().get_position() == element_position_fixed) + { + ret = el->get_element_by_point(client_x, client_y, client_x, client_y); + if(!ret && (*i)->is_point_inside(client_x, client_y)) + { + ret = (*i)->src_el(); + } + } else + { + ret = el->get_element_by_point(el_pos.x, el_pos.y, client_x, client_y); + if(!ret && (*i)->is_point_inside(el_pos.x, el_pos.y)) + { + ret = (*i)->src_el(); + } + } + el = nullptr; + } + break; + case draw_block: + if(!el->src_el()->is_inline_box() && el->src_el()->css().get_float() == float_none && !el->src_el()->is_positioned()) + { + if(el->is_point_inside(el_pos.x, el_pos.y)) + { + ret = el->src_el(); + } + } + break; + case draw_floats: + if(el->src_el()->css().get_float() != float_none && !el->src_el()->is_positioned()) + { + ret = el->get_element_by_point(el_pos.x, el_pos.y, client_x, client_y); + + if(!ret && (*i)->is_point_inside(el_pos.x, el_pos.y)) + { + ret = (*i)->src_el(); + } + el = nullptr; + } + break; + case draw_inlines: + if(el->src_el()->is_inline_box() && el->src_el()->css().get_float() == float_none && !el->src_el()->is_positioned()) + { + if(el->src_el()->css().get_display() == display_inline_block || + el->src_el()->css().get_display() == display_inline_table || + el->src_el()->css().get_display() == display_inline_flex) + { + ret = el->get_element_by_point(el_pos.x, el_pos.y, client_x, client_y); + el = nullptr; + } + if(!ret && (*i)->is_point_inside(el_pos.x, el_pos.y)) + { + ret = (*i)->src_el(); + } + } + break; + default: + break; + } + + if(el && !el->src_el()->is_positioned()) + { + if(flag == draw_positioned) + { + element::ptr child = el->get_child_by_point(el_pos.x, el_pos.y, client_x, client_y, flag, zindex); + if(child) + { + ret = child; + } + } else + { + if( el->src_el()->css().get_float() == float_none && + el->src_el()->css().get_display() != display_inline_block) + { + element::ptr child = el->get_child_by_point(el_pos.x, el_pos.y, client_x, client_y, flag, zindex); + if(child) + { + ret = child; + } + } + } + } + } + } + + return ret; +} + +std::shared_ptr<litehtml::element> litehtml::render_item::get_element_by_point(int x, int y, int client_x, int client_y) +{ + if(!is_visible()) return nullptr; + + element::ptr ret; + + std::map<int, bool> z_indexes; + + for(const auto& i : m_positioned) + { + z_indexes[i->src_el()->css().get_z_index()]; + } + + for(const auto& zindex : z_indexes) + { + if(zindex.first > 0) + { + ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, zindex.first); + break; + } + } + 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; + + ret = get_child_by_point(x, y, client_x, client_y, draw_inlines, 0); + if(ret) return ret; + + ret = get_child_by_point(x, y, client_x, client_y, draw_floats, 0); + if(ret) return ret; + + ret = get_child_by_point(x, y, client_x, client_y, draw_block, 0); + 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(src_el()->css().get_position() == element_position_fixed) + { + if(is_point_inside(client_x, client_y)) + { + ret = src_el(); + } + } else + { + if(is_point_inside(x, y)) + { + ret = src_el(); + } + } + + return ret; +} + +bool litehtml::render_item::is_point_inside( int x, int y ) +{ + if(src_el()->css().get_display() != display_inline && src_el()->css().get_display() != display_table_row) + { + position pos = m_pos; + pos += m_padding; + pos += m_borders; + if(pos.is_point_inside(x, y)) + { + return true; + } else + { + return false; + } + } else + { + position::vector boxes; + get_inline_boxes(boxes); + for(auto & box : boxes) + { + if(box.is_point_inside(x, y)) + { + return true; + } + } + } + return false; +} + +void litehtml::render_item::get_rendering_boxes( position::vector& redraw_boxes) +{ + if(src_el()->css().get_display() == display_inline || src_el()->css().get_display() == display_table_row) + { + get_inline_boxes(redraw_boxes); + } else + { + position pos = m_pos; + pos += m_padding; + pos += m_borders; + redraw_boxes.push_back(pos); + } + + if(src_el()->css().get_position() != element_position_fixed) + { + auto cur_el = parent(); + while(cur_el) + { + for(auto& box : redraw_boxes) + { + box.x += cur_el->m_pos.x; + box.y += cur_el->m_pos.y; + } + cur_el = cur_el->parent(); + } + } +} + +void litehtml::render_item::dump(litehtml::dumper& cout) +{ + cout.begin_node(src_el()->dump_get_name() + "{" + string(typeid(*this).name()) + "}"); + + auto attrs = src_el()->dump_get_attrs(); + if(!attrs.empty()) + { + cout.begin_attrs_group("attributes"); + for (const auto &attr: attrs) + { + cout.add_attr(std::get<0>(attr), std::get<1>(attr)); + } + cout.end_attrs_group(); + } + + if(!m_children.empty()) + { + cout.begin_attrs_group("children"); + for (const auto &el: m_children) + { + el->dump(cout); + } + cout.end_attrs_group(); + } + + cout.end_node(); +} + +litehtml::position litehtml::render_item::get_placement() const +{ + litehtml::position pos = m_pos; + auto cur_el = parent(); + while(cur_el) + { + pos.x += cur_el->m_pos.x; + pos.y += cur_el->m_pos.y; + cur_el = cur_el->parent(); + } + return pos; +} + +std::shared_ptr<litehtml::render_item> litehtml::render_item::init() +{ + src_el()->add_render(shared_from_this()); + + for(auto& el : children()) + { + el = el->init(); + } + + return shared_from_this(); +} + +void litehtml::render_item::calc_cb_length(const css_length& len, int percent_base, containing_block_context::typed_int& out_value) const +{ + if (!len.is_predefined()) + { + if(len.units() == litehtml::css_units_percentage) + { + out_value.value = len.calc_percent(percent_base); + out_value.type = litehtml::containing_block_context::cbc_value_type_percentage; + } else + { + out_value.value = src_el()->get_document()->to_pixels(len, src_el()->css().get_font_size()); + out_value.type = containing_block_context::cbc_value_type_absolute; + } + } +} + +litehtml::containing_block_context litehtml::render_item::calculate_containing_block_context(const containing_block_context& cb_context) +{ + containing_block_context ret; + ret.context_idx = cb_context.context_idx + 1; + ret.width.value = ret.max_width.value = cb_context.width.value - content_offset_width(); + if(src_el()->css().get_position() != element_position_absolute && src_el()->css().get_position() != element_position_fixed) + { + ret.height.value = cb_context.height.value - content_offset_height(); + } + + // Calculate width if css property is not auto + // 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); + 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(); + } + if (ret.height.type != containing_block_context::cbc_value_type_auto && (src_el()->css().get_display() == display_table || src_el()->is_root())) + { + ret.height.value -= content_offset_height(); + } + } + ret.render_width = ret.width; + + calc_cb_length(src_el()->css().get_min_width(), cb_context.width, ret.min_width); + calc_cb_length(src_el()->css().get_max_width(), cb_context.width, ret.max_width); + + 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) + { + 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(); + } + } + return ret; +} + +void litehtml::render_item_table_row::get_inline_boxes( position::vector& boxes ) const +{ + position pos; + for(auto& el : m_children) + { + if(el->src_el()->css().get_display() == display_table_cell) + { + pos.x = el->left() + el->margin_left(); + pos.y = el->top() - m_padding.top - m_borders.top; + + pos.width = el->right() - pos.x - el->margin_right() - el->margin_left(); + pos.height = el->height() + m_padding.top + m_padding.bottom + m_borders.top + m_borders.bottom; + + boxes.push_back(pos); + } + } +} diff --git a/src/plugins/litehtml_viewer/litehtml/render_item.h b/src/plugins/litehtml_viewer/litehtml/render_item.h @@ -0,0 +1,599 @@ +#ifndef LH_RENDER_ITEM_H +#define LH_RENDER_ITEM_H + +#include <memory> +#include <utility> +#include <list> +#include <tuple> +#include "types.h" +#include "line_box.h" +#include "table.h" + +namespace litehtml +{ + class element; + + class render_item : public std::enable_shared_from_this<render_item> + { + protected: + std::shared_ptr<element> m_element; + std::weak_ptr<render_item> m_parent; + std::list<std::shared_ptr<render_item>> m_children; + margins m_margins; + margins m_padding; + margins m_borders; + position m_pos; + bool m_skip; + std::vector<std::shared_ptr<render_item>> m_positioned; + + containing_block_context calculate_containing_block_context(const containing_block_context& cb_context); + void calc_cb_length(const css_length& len, int percent_base, containing_block_context::typed_int& out_value) const; + + public: + explicit render_item(std::shared_ptr<element> src_el); + + virtual ~render_item() = default; + + std::list<std::shared_ptr<render_item>>& children() + { + return m_children; + } + + position& pos() + { + return m_pos; + } + + bool skip() const + { + return m_skip; + } + + void skip(bool val) + { + m_skip = val; + } + + int right() const + { + return left() + width(); + } + + int left() const + { + return m_pos.left() - m_margins.left - m_padding.left - m_borders.left; + } + + int top() const + { + return m_pos.top() - m_margins.top - m_padding.top - m_borders.top; + } + + int bottom() const + { + return top() + height(); + } + + int height() const + { + return m_pos.height + m_margins.height() + m_padding.height() + m_borders.height(); + } + + int width() const + { + return m_pos.width + m_margins.left + m_margins.right + m_padding.width() + m_borders.width(); + } + + int padding_top() const + { + return m_padding.top; + } + + int padding_bottom() const + { + return m_padding.bottom; + } + + int padding_left() const + { + return m_padding.left; + } + + int padding_right() const + { + return m_padding.right; + } + + int border_top() const + { + return m_borders.top; + } + + int border_bottom() const + { + return m_borders.bottom; + } + + int border_left() const + { + return m_borders.left; + } + + int border_right() const + { + return m_borders.right; + } + + int margin_top() const + { + return m_margins.top; + } + + int margin_bottom() const + { + return m_margins.bottom; + } + + int margin_left() const + { + return m_margins.left; + } + + int margin_right() const + { + return m_margins.right; + } + + std::shared_ptr<render_item> parent() const + { + return m_parent.lock(); + } + + margins& get_margins() + { + return m_margins; + } + + margins& get_paddings() + { + return m_padding; + } + + void set_paddings(const margins& val) + { + m_padding = val; + } + + margins& get_borders() + { + return m_borders; + } + + /** + * Top offset to the element content. Includes paddings, margins and borders. + */ + int content_offset_top() const + { + return m_margins.top + m_padding.top + m_borders.top; + } + + /** + * Bottom offset to the element content. Includes paddings, margins and borders. + */ + inline int content_offset_bottom() const + { + return m_margins.bottom + m_padding.bottom + m_borders.bottom; + } + + /** + * Left offset to the element content. Includes paddings, margins and borders. + */ + int content_offset_left() const + { + return m_margins.left + m_padding.left + m_borders.left; + } + + /** + * Right offset to the element content. Includes paddings, margins and borders. + */ + int content_offset_right() const + { + return m_margins.right + m_padding.right + m_borders.right; + } + + /** + * Sum of left and right offsets to the element content. Includes paddings, margins and borders. + */ + int content_offset_width() const + { + return content_offset_left() + content_offset_right(); + } + + /** + * Sum of top and bottom offsets to the element content. Includes paddings, margins and borders. + */ + int content_offset_height() const + { + return content_offset_top() + content_offset_bottom(); + } + + int box_sizing_left() const + { + return m_padding.left + m_borders.left; + } + + int box_sizing_right() const + { + return m_padding.right + m_borders.right; + } + + int box_sizing_width() const + { + return box_sizing_left() + box_sizing_left(); + } + + int box_sizing_top() const + { + return m_padding.top + m_borders.top; + } + + int box_sizing_bottom() const + { + return m_padding.bottom + m_borders.bottom; + } + + int box_sizing_height() const + { + return box_sizing_top() + box_sizing_bottom(); + } + + void parent(const std::shared_ptr<render_item>& par) + { + m_parent = par; + } + + const std::shared_ptr<element>& src_el() const + { + return m_element; + } + + const css_properties& css() const + { + return m_element->css(); + } + + void add_child(const std::shared_ptr<render_item>& ri) + { + m_children.push_back(ri); + ri->parent(shared_from_this()); + } + + virtual int render(int x, int y, const containing_block_context& containing_block_size, bool second_pass = false) + { + return 0; + } + + bool is_root() const + { + return m_parent.expired(); + } + + bool collapse_top_margin() const + { + return !m_borders.top && + !m_padding.top && + m_element->in_normal_flow() && + m_element->css().get_float() == float_none && + m_margins.top >= 0 && + !is_root(); + } + + bool collapse_bottom_margin() const + { + return !m_borders.bottom && + !m_padding.bottom && + m_element->in_normal_flow() && + m_element->css().get_float() == float_none && + m_margins.bottom >= 0 && + !is_root(); + } + + bool is_visible() const + { + return !(m_skip || src_el()->css().get_display() == display_none || src_el()->css().get_visibility() != visibility_visible); + } + + 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; } + virtual std::shared_ptr<render_item> clone() + { + return std::make_shared<render_item>(src_el()); + } + std::tuple< + std::shared_ptr<litehtml::render_item>, + std::shared_ptr<litehtml::render_item>, + std::shared_ptr<litehtml::render_item> + > split_inlines(); + bool fetch_positioned(); + void render_positioned(render_type rt = render_all); + void add_positioned(const std::shared_ptr<litehtml::render_item> &el); + void get_redraw_box(litehtml::position& pos, int x = 0, int y = 0); + void calc_document_size( litehtml::size& sz, litehtml::size& content_size, int x = 0, int y = 0 ); + virtual void get_inline_boxes( position::vector& boxes ) const {}; + virtual void set_inline_boxes( position::vector& boxes ) {}; + virtual void add_inline_box( const position& box ) {}; + virtual void clear_inline_boxes() {}; + void draw_stacking_context( uint_ptr hdc, int x, int y, const position* clip, bool with_positioned ); + virtual void draw_children( uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex ); + virtual int get_draw_vertical_offset() { return 0; } + virtual std::shared_ptr<element> get_child_by_point(int x, int y, int client_x, int client_y, draw_flag flag, int zindex); + std::shared_ptr<element> get_element_by_point(int x, int y, int client_x, int client_y); + bool is_point_inside( int x, int y ); + void dump(litehtml::dumper& cout); + position get_placement() const; + /** + * Returns the boxes of rendering element. All coordinates are absolute + * + * @param redraw_boxes [out] resulting rendering boxes + * @return + */ + void get_rendering_boxes( position::vector& redraw_boxes); + + virtual void get_line_left_right( int y, int def_right, int& ln_left, int& ln_right ) {} + virtual int get_line_left( int y ) { return 0; } + virtual int get_line_right( int y, int def_right ) { return 0; } + virtual int get_left_floats_height() const { return 0; } + virtual int get_right_floats_height() const { return 0; } + virtual int get_floats_height(element_float el_float = float_none) const { return 0; } + virtual int find_next_line_top( int top, int width, int def_right ) { return 0; } + virtual void add_float(const std::shared_ptr<render_item> &el, int x, int y, int context) {} + virtual void clear_floats(int context) {} + virtual void update_floats(int dy, const std::shared_ptr<render_item> &_parent) {} + }; + + class render_item_block : public render_item + { + protected: + std::list<floated_box> m_floats_left; + std::list<floated_box> m_floats_right; + int_int_cache m_cache_line_left; + int_int_cache m_cache_line_right; + + /** + * Render block content. + * + * @param x - horizontal position of the content + * @param y - vertical position of the content + * @param second_pass - true is this is the second pass. + * @param ret_width - input minimal width. + * @param self_size - defines calculated size of block + * @return return value is the minimal width of the content in block. Must be greater or equal to ret_width parameter + */ + virtual int _render_content(int x, int y, bool second_pass, int ret_width, const containing_block_context &self_size) {return ret_width;} + int render(int x, int y, const containing_block_context &containing_block_size, bool second_pass) override; + + int place_float(const std::shared_ptr<render_item> &el, int top, const containing_block_context &self_size); + int get_floats_height(element_float el_float = float_none) const override; + int get_left_floats_height() const override; + int get_right_floats_height() const override; + int get_line_left( int y ) override; + int get_line_right( int y, int def_right ) override; + void get_line_left_right( int y, int def_right, int& ln_left, int& ln_right ) override; + void add_float(const std::shared_ptr<render_item> &el, int x, int y, int context) override; + void clear_floats(int context) override; + int get_cleared_top(const std::shared_ptr<render_item> &el, int line_top) const; + int find_next_line_top( int top, int width, int def_right ) override; + virtual void fix_line_width(element_float flt, + const containing_block_context &containing_block_size) + {} + void update_floats(int dy, const std::shared_ptr<render_item> &_parent) override; + public: + explicit render_item_block(std::shared_ptr<element> src_el) : render_item(std::move(src_el)) + {} + + std::shared_ptr<render_item> clone() override + { + return std::make_shared<render_item_block>(src_el()); + } + std::shared_ptr<render_item> init() override; + }; + + /** + * In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a + * containing block. + * https://www.w3.org/TR/CSS22/visuren.html#block-formatting + */ + class render_item_block_context : public render_item_block + { + protected: + int _render_content(int x, int y, bool second_pass, int ret_width, + const containing_block_context &self_size) override; + + public: + explicit render_item_block_context(std::shared_ptr<element> src_el) : render_item_block(std::move(src_el)) + {} + + std::shared_ptr<render_item> clone() override + { + return std::make_shared<render_item_block_context>(src_el()); + } + }; + + /** + * An inline formatting context is established by a block container box that contains no block-level boxes. + * https://www.w3.org/TR/CSS22/visuren.html#inline-formatting + */ + class render_item_inline_context : public render_item_block + { + /** + * Structure contains elements with display: inline + * members: + * - element: render_item with display: inline + * - boxes: rectangles represented inline element content. There are can be many boxes if content + * is split into some lines + * - start_box: the start position of currently calculated box + */ + struct inlines_item + { + std::shared_ptr<render_item> element; + position::vector boxes; + position start_box; + + explicit inlines_item(const std::shared_ptr<render_item>& el) : element(el) {} + }; + protected: + std::vector<std::unique_ptr<litehtml::line_box> > m_line_boxes; + int m_max_line_width; + + int _render_content(int x, int y, bool second_pass, int ret_width, + const containing_block_context &self_size) override; + void fix_line_width(element_float flt, + const containing_block_context &self_size) override; + + std::list<std::unique_ptr<line_box_item> > finish_last_box(bool end_of_render, const containing_block_context &self_size); + void place_inline(std::unique_ptr<line_box_item> item, const containing_block_context &self_size); + int new_box(const std::unique_ptr<line_box_item>& el, line_context& line_ctx, const containing_block_context &self_size); + void apply_vertical_align() override; + public: + explicit render_item_inline_context(std::shared_ptr<element> src_el) : render_item_block(std::move(src_el)), m_max_line_width(0) + {} + + std::shared_ptr<render_item> clone() override + { + return std::make_shared<render_item_inline_context>(src_el()); + } + + int get_base_line() override; + }; + + class render_item_table : public render_item + { + protected: + // data for table rendering + std::unique_ptr<table_grid> m_grid; + int m_border_spacing_x; + int m_border_spacing_y; + + public: + explicit render_item_table(std::shared_ptr<element> src_el); + + std::shared_ptr<render_item> clone() override + { + return std::make_shared<render_item_table>(src_el()); + } + int render(int x, int y, const containing_block_context &containing_block_size, bool second_pass) override; + void draw_children(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex) override; + int get_draw_vertical_offset() override; + std::shared_ptr<render_item> init() override; + }; + + class render_item_table_part : public render_item + { + public: + explicit render_item_table_part(std::shared_ptr<element> src_el) : render_item(std::move(src_el)) + {} + + std::shared_ptr<render_item> clone() override + { + return std::make_shared<render_item_table_part>(src_el()); + } + }; + + class render_item_table_row : public render_item + { + public: + explicit render_item_table_row(std::shared_ptr<element> src_el) : render_item(std::move(src_el)) + {} + + std::shared_ptr<render_item> clone() override + { + return std::make_shared<render_item_table_row>(src_el()); + } + void get_inline_boxes( position::vector& boxes ) const override; + }; + + class render_item_inline : public render_item + { + protected: + position::vector m_boxes; + + public: + explicit render_item_inline(std::shared_ptr<element> src_el) : render_item(std::move(src_el)) + {} + + void get_inline_boxes( position::vector& boxes ) const override { boxes = m_boxes; } + 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(); } + + std::shared_ptr<render_item> clone() override + { + return std::make_shared<render_item_inline>(src_el()); + } + }; + + class render_item_image : public render_item + { + protected: + int calc_max_height(int image_height, int containing_block_height); + + public: + explicit render_item_image(std::shared_ptr<element> src_el) : render_item(std::move(src_el)) + {} + + int render(int x, int y, const containing_block_context &containing_block_size, bool second_pass) override; + std::shared_ptr<render_item> clone() override + { + return std::make_shared<render_item_image>(src_el()); + } + }; + + 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; + + int _render_content(int x, int y, bool second_pass, int ret_width, + const containing_block_context &self_size) override; + + public: + explicit render_item_flex(std::shared_ptr<element> src_el) : render_item_block(std::move(src_el)) + {} + + std::shared_ptr<render_item> clone() override + { + 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; + }; + +} + +#endif //LH_RENDER_ITEM_H diff --git a/src/plugins/litehtml_viewer/litehtml/render_table.cpp b/src/plugins/litehtml_viewer/litehtml/render_table.cpp @@ -0,0 +1,482 @@ +#include "html.h" +#include "render_item.h" +#include "document.h" +#include "iterators.h" + + +litehtml::render_item_table::render_item_table(std::shared_ptr<element> _src_el) : + render_item(std::move(_src_el)), + m_border_spacing_x(0), + m_border_spacing_y(0) +{ +} + +int litehtml::render_item_table::render(int x, int y, const containing_block_context &containing_block_size, bool second_pass) +{ + if (!m_grid) return 0; + + calc_outlines(containing_block_size.width); + + m_pos.clear(); + m_pos.move_to(x, y); + + m_pos.x += content_offset_left(); + m_pos.y += content_offset_top(); + + containing_block_context self_size = calculate_containing_block_context(containing_block_size); + +/* if(cb_size.width_type == containing_block_context::cbc_value_type_auto) + { + max_width -= content_offset_left() + content_offset_right(); + } else + { + max_width -= m_padding.width() + m_borders.width(); + } + if(max_width < 0) max_width = 0; + max_width = std::min(max_width, cb_size.width); +*/ + // Calculate table spacing + int table_width_spacing = 0; + if (src_el()->css().get_border_collapse() == border_collapse_separate) + { + table_width_spacing = m_border_spacing_x * (m_grid->cols_count() + 1); + } + else + { + table_width_spacing = 0; + + if (m_grid->cols_count()) + { + table_width_spacing -= std::min(border_left(), m_grid->column(0).border_left); + table_width_spacing -= std::min(border_right(), m_grid->column(m_grid->cols_count() - 1).border_right); + } + + for (int col = 1; col < m_grid->cols_count(); col++) + { + table_width_spacing -= std::min(m_grid->column(col).border_left, m_grid->column(col - 1).border_right); + } + } + + + // Calculate the minimum content width (MCW) of each cell: the formatted content may span any number of lines but may not overflow the cell box. + // If the specified 'width' (W) of the cell is greater than MCW, W is the minimum cell width. A value of 'auto' means that MCW is the minimum + // cell width. + // + // Also, calculate the "maximum" cell width of each cell: formatting the content without breaking lines other than where explicit line breaks occur. + + if (m_grid->cols_count() == 1 && self_size.width.type != containing_block_context::cbc_value_type_auto) + { + for (int row = 0; row < m_grid->rows_count(); row++) + { + table_cell* cell = m_grid->cell(0, row); + if (cell && cell->el) + { + cell->min_width = cell->max_width = cell->el->render(0, 0, self_size.new_width(self_size.render_width - table_width_spacing)); + cell->el->pos().width = cell->min_width - cell->el->content_offset_left() - + cell->el->content_offset_right(); + } + } + } + else + { + for (int row = 0; row < m_grid->rows_count(); row++) + { + for (int col = 0; col < m_grid->cols_count(); col++) + { + table_cell* cell = m_grid->cell(col, row); + if (cell && cell->el) + { + if (!m_grid->column(col).css_width.is_predefined() && m_grid->column(col).css_width.units() != css_units_percentage) + { + int css_w = m_grid->column(col).css_width.calc_percent(self_size.width); + int el_w = cell->el->render(0, 0, self_size.new_width(css_w)); + cell->min_width = cell->max_width = std::max(css_w, el_w); + cell->el->pos().width = cell->min_width - cell->el->content_offset_left() - + cell->el->content_offset_right(); + } + else + { + // calculate minimum content width + cell->min_width = cell->el->render(0, 0, self_size.new_width(cell->el->content_offset_width())); + // calculate maximum content width + cell->max_width = cell->el->render(0, 0, self_size.new_width(self_size.render_width - table_width_spacing)); + } + } + } + } + } + + // For each column, determine a maximum and minimum column width from the cells that span only that column. + // The minimum is that required by the cell with the largest minimum cell width (or the column 'width', whichever is larger). + // The maximum is that required by the cell with the largest maximum cell width (or the column 'width', whichever is larger). + + for (int col = 0; col < m_grid->cols_count(); col++) + { + m_grid->column(col).max_width = 0; + m_grid->column(col).min_width = 0; + for (int row = 0; row < m_grid->rows_count(); row++) + { + if (m_grid->cell(col, row)->colspan <= 1) + { + m_grid->column(col).max_width = std::max(m_grid->column(col).max_width, m_grid->cell(col, row)->max_width); + m_grid->column(col).min_width = std::max(m_grid->column(col).min_width, m_grid->cell(col, row)->min_width); + } + } + } + + // For each cell that spans more than one column, increase the minimum widths of the columns it spans so that together, + // they are at least as wide as the cell. Do the same for the maximum widths. + // If possible, widen all spanned columns by approximately the same amount. + + for (int col = 0; col < m_grid->cols_count(); col++) + { + for (int row = 0; row < m_grid->rows_count(); row++) + { + if (m_grid->cell(col, row)->colspan > 1) + { + int max_total_width = m_grid->column(col).max_width; + int min_total_width = m_grid->column(col).min_width; + for (int col2 = col + 1; col2 < col + m_grid->cell(col, row)->colspan; col2++) + { + max_total_width += m_grid->column(col2).max_width; + min_total_width += m_grid->column(col2).min_width; + } + if (min_total_width < m_grid->cell(col, row)->min_width) + { + m_grid->distribute_min_width(m_grid->cell(col, row)->min_width - min_total_width, col, col + m_grid->cell(col, row)->colspan - 1); + } + if (max_total_width < m_grid->cell(col, row)->max_width) + { + m_grid->distribute_max_width(m_grid->cell(col, row)->max_width - max_total_width, col, col + m_grid->cell(col, row)->colspan - 1); + } + } + } + } + + // If the 'table' or 'inline-table' element's 'width' property has a computed value (W) other than 'auto', the used width is the + // greater of W, CAPMIN, and the minimum width required by all the columns plus cell spacing or borders (MIN). + // If the used width is greater than MIN, the extra width should be distributed over the columns. + // + // If the 'table' or 'inline-table' element has 'width: auto', the used width is the greater of the table's containing block width, + // CAPMIN, and MIN. However, if either CAPMIN or the maximum width required by the columns plus cell spacing or borders (MAX) is + // less than that of the containing block, use max(MAX, CAPMIN). + + + int table_width = 0; + int min_table_width = 0; + int max_table_width = 0; + + if (self_size.width.type == containing_block_context::cbc_value_type_absolute) + { + table_width = m_grid->calc_table_width(self_size.render_width - table_width_spacing, false, min_table_width, max_table_width); + } + else + { + table_width = m_grid->calc_table_width(self_size.render_width - table_width_spacing, self_size.width.type == containing_block_context::cbc_value_type_auto, min_table_width, max_table_width); + } + + min_table_width += table_width_spacing; + max_table_width += table_width_spacing; + table_width += table_width_spacing; + m_grid->calc_horizontal_positions(m_borders, src_el()->css().get_border_collapse(), m_border_spacing_x); + + bool row_span_found = false; + + // render cells with computed width + for (int row = 0; row < m_grid->rows_count(); row++) + { + m_grid->row(row).height = 0; + for (int col = 0; col < m_grid->cols_count(); col++) + { + table_cell* cell = m_grid->cell(col, row); + if (cell->el) + { + int span_col = col + cell->colspan - 1; + if (span_col >= m_grid->cols_count()) + { + span_col = m_grid->cols_count() - 1; + } + int cell_width = m_grid->column(span_col).right - m_grid->column(col).left; + + //if (cell->el->pos().width != cell_width - cell->el->content_offset_left() - + // cell->el->content_offset_right()) + { + cell->el->render(m_grid->column(col).left, 0, self_size.new_width(cell_width), true); + cell->el->pos().width = cell_width - cell->el->content_offset_left() - + cell->el->content_offset_right(); + } + /*else + { + cell->el->pos().x = m_grid->column(col).left + cell->el->content_offset_left(); + }*/ + + if (cell->rowspan <= 1) + { + m_grid->row(row).height = std::max(m_grid->row(row).height, cell->el->height()); + } + else + { + row_span_found = true; + } + + } + } + } + + if (row_span_found) + { + for (int col = 0; col < m_grid->cols_count(); col++) + { + for (int row = 0; row < m_grid->rows_count(); row++) + { + table_cell* cell = m_grid->cell(col, row); + if (cell->el) + { + int span_row = row + cell->rowspan - 1; + if (span_row >= m_grid->rows_count()) + { + span_row = m_grid->rows_count() - 1; + } + if (span_row != row) + { + int h = 0; + for (int i = row; i <= span_row; i++) + { + h += m_grid->row(i).height; + } + if (h < cell->el->height()) + { + m_grid->row(span_row).height += cell->el->height() - h; + } + } + } + } + } + } + + // Calculate vertical table spacing + int table_height_spacing = 0; + if (src_el()->css().get_border_collapse() == border_collapse_separate) + { + table_height_spacing = m_border_spacing_y * (m_grid->rows_count() + 1); + } + else + { + table_height_spacing = 0; + + if (m_grid->rows_count()) + { + table_height_spacing -= std::min(border_top(), m_grid->row(0).border_top); + table_height_spacing -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom); + } + + for (int row = 1; row < m_grid->rows_count(); row++) + { + table_height_spacing -= std::min(m_grid->row(row).border_top, m_grid->row(row - 1).border_bottom); + } + } + + + // calculate block height + int block_height = 0; + if (get_predefined_height(block_height, containing_block_size.height)) + { + block_height -= m_padding.height() + m_borders.height(); + } + + // calculate minimum height from m_css.get_min_height() + int min_height = 0; + if (!src_el()->css().get_min_height().is_predefined() && src_el()->css().get_min_height().units() == css_units_percentage) + { + min_height = src_el()->css().get_min_height().calc_percent(containing_block_size.height); + } + else + { + min_height = (int)src_el()->css().get_min_height().val(); + } + + int minimum_table_height = std::max(block_height, min_height); + + m_grid->calc_rows_height(minimum_table_height - table_height_spacing, m_border_spacing_y); + m_grid->calc_vertical_positions(m_borders, src_el()->css().get_border_collapse(), m_border_spacing_y); + + int table_height = 0; + + // place cells vertically + for (int col = 0; col < m_grid->cols_count(); col++) + { + for (int row = 0; row < m_grid->rows_count(); row++) + { + table_cell* cell = m_grid->cell(col, row); + if (cell->el) + { + int span_row = row + cell->rowspan - 1; + if (span_row >= m_grid->rows_count()) + { + span_row = m_grid->rows_count() - 1; + } + cell->el->pos().y = m_grid->row(row).top + cell->el->content_offset_top(); + cell->el->pos().height = m_grid->row(span_row).bottom - m_grid->row(row).top - + cell->el->content_offset_top() - + cell->el->content_offset_bottom(); + table_height = std::max(table_height, m_grid->row(span_row).bottom); + cell->el->apply_vertical_align(); + } + } + } + + if (src_el()->css().get_border_collapse() == border_collapse_collapse) + { + if (m_grid->rows_count()) + { + table_height -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom); + } + } + else + { + table_height += m_border_spacing_y; + } + + // Render table captions + // Table border doesn't round the caption, so we have to start caption in the border position + int captions_height = -border_top(); + + for (auto& caption : m_grid->captions()) + { + caption->render(-border_left(), captions_height, self_size.new_width(table_width + border_left() + border_right())); + captions_height += caption->height(); + } + + if (captions_height) + { + // Add border height to get the top of cells + captions_height += border_top(); + + // Save caption height for draw_background + m_grid->captions_height(captions_height); + + // Move table cells to the bottom side + for (int row = 0; row < m_grid->rows_count(); row++) + { + m_grid->row(row).el_row->pos().y += captions_height; + for (int col = 0; col < m_grid->cols_count(); col++) + { + table_cell* cell = m_grid->cell(col, row); + if (cell->el) + { + cell->el->pos().y += captions_height; + } + } + } + } + + m_pos.move_to(x + content_offset_left(), y + content_offset_top()); + m_pos.width = table_width; + m_pos.height = table_height + captions_height; + + if(self_size.width.type != containing_block_context::cbc_value_type_absolute) + { + return std::min(table_width, max_table_width) + content_offset_width(); + } + return table_width + content_offset_width(); +} + +std::shared_ptr<litehtml::render_item> litehtml::render_item_table::init() +{ + // Initialize Grid + m_grid = std::unique_ptr<table_grid>(new table_grid()); + + go_inside_table table_selector; + table_rows_selector row_selector; + table_cells_selector cell_selector; + + elements_iterator row_iter(false, &table_selector, &row_selector); + + row_iter.process(shared_from_this(), [&](std::shared_ptr<render_item>& el, iterator_item_type item_type) + { + m_grid->begin_row(el); + + + elements_iterator cell_iter(true, &table_selector, &cell_selector); + cell_iter.process(el, [&](std::shared_ptr<render_item>& el, iterator_item_type item_type) + { + if(item_type != iterator_item_type_end_parent) + { + el = el->init(); + m_grid->add_cell(el); + } + }); + }); + + for (auto& el : m_children) + { + if (el->src_el()->css().get_display() == display_table_caption) + { + el = el->init(); + m_grid->captions().push_back(el); + } + } + + m_grid->finish(); + + if(src_el()->css().get_border_collapse() == border_collapse_separate) + { + int font_size = src_el()->css().get_font_size(); + document::ptr doc = src_el()->get_document(); + m_border_spacing_x = doc->to_pixels(src_el()->css().get_border_spacing_x(), font_size); + m_border_spacing_y = doc->to_pixels(src_el()->css().get_border_spacing_y(), font_size); + } else + { + m_border_spacing_x = 0; + m_border_spacing_y = 0; + } + + src_el()->add_render(shared_from_this()); + + return shared_from_this(); +} + +void litehtml::render_item_table::draw_children(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex) +{ + if (!m_grid) return; + + position pos = m_pos; + pos.x += x; + pos.y += y; + for (auto& caption : m_grid->captions()) + { + if (flag == draw_block) + { + caption->src_el()->draw(hdc, pos.x, pos.y, clip, caption); + } + caption->draw_children(hdc, pos.x, pos.y, clip, flag, zindex); + } + for (int row = 0; row < m_grid->rows_count(); row++) + { + if (flag == draw_block) + { + m_grid->row(row).el_row->src_el()->draw_background(hdc, pos.x, pos.y, clip, m_grid->row(row).el_row); + } + for (int col = 0; col < m_grid->cols_count(); col++) + { + table_cell* cell = m_grid->cell(col, row); + if (cell->el) + { + if (flag == draw_block) + { + cell->el->src_el()->draw(hdc, pos.x, pos.y, clip, cell->el); + } + cell->el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex); + } + } + } +} + +int litehtml::render_item_table::get_draw_vertical_offset() +{ + if(m_grid) + { + return m_grid->captions_height(); + } + return 0; +} diff --git a/src/plugins/litehtml_viewer/litehtml/string_id.cpp b/src/plugins/litehtml_viewer/litehtml/string_id.cpp @@ -0,0 +1,54 @@ +#include "html.h" +#include "string_id.h" +#include <assert.h> + +#ifndef LITEHTML_NO_THREADS + #include <mutex> + static std::mutex mutex; + #define lock_guard std::lock_guard<std::mutex> lock(mutex) +#else + #define lock_guard +#endif + +namespace litehtml +{ + +static std::map<string, string_id> map; +static std::vector<string> array; + +static int init() +{ + string_vector names; + split_string(initial_string_ids, names, ","); + for (auto& name : names) + { + trim(name); + assert(name[0] == '_' && name.back() == '_'); + name = name.substr(1, name.size() - 2); // _border_color_ -> border_color + std::replace(name.begin(), name.end(), '_', '-'); // border_color -> border-color + _id(name); // this will create association _border_color_ <-> "border-color" + } + return 0; +} +static int dummy = init(); + +const string_id empty_id = _id(""); +const string_id star_id = _id("*"); + +string_id _id(const string& str) +{ + lock_guard; + auto it = map.find(str); + if (it != map.end()) return it->second; + // else: str not found, add it to the array and the map + array.push_back(str); + return map[str] = (string_id)(array.size() - 1); +} + +const string& _s(string_id id) +{ + lock_guard; + return array[id]; +} + +} // namespace litehtml +\ No newline at end of file diff --git a/src/plugins/litehtml_viewer/litehtml/string_id.h b/src/plugins/litehtml_viewer/litehtml/string_id.h @@ -0,0 +1,300 @@ +#ifndef LH_STRING_ID_H +#define LH_STRING_ID_H + +namespace litehtml +{ + +#define STRING_ID(...)\ + enum string_id { __VA_ARGS__ };\ + const auto initial_string_ids = #__VA_ARGS__; + +STRING_ID( + + // HTML tags + _a_, + _abbr_, + _acronym_, + _address_, + _applet_, + _area_, + _article_, + _aside_, + _audio_, + _b_, + _base_, + _basefont_, + _bdi_, + _bdo_, + _big_, + _blockquote_, + _body_, + _br_, + _button_, + _canvas_, + _caption_, + _center_, + _cite_, + _code_, + _col_, + _colgroup_, + _data_, + _datalist_, + _dd_, + _del_, + _details_, + _dfn_, + _dialog_, + _dir_, + _div_, + _dl_, + _dt_, + _em_, + _embed_, + _fieldset_, + _figcaption_, + _figure_, + _footer_, + _form_, + _frame_, + _frameset_, + _h1_, + _h2_, + _h3_, + _h4_, + _h5_, + _h6_, + _head_, + _header_, + _hr_, + _html_, + _i_, + _iframe_, + _img_, + _input_, + _ins_, + _kbd_, + _label_, + _legend_, + _li_, + _link_, + _main_, + _map_, + _mark_, + _meta_, + _meter_, + _nav_, + _noframes_, + _noscript_, + _object_, + _ol_, + _optgroup_, + _option_, + _output_, + _p_, + _param_, + _picture_, + _pre_, + _progress_, + _q_, + _rp_, + _rt_, + _ruby_, + _s_, + _samp_, + _script_, + _section_, + _select_, + _small_, + _source_, + _span_, + _strike_, + _strong_, + _style_, + _sub_, + _summary_, + _sup_, + _svg_, + _table_, + _tbody_, + _td_, + _template_, + _textarea_, + _tfoot_, + _th_, + _thead_, + _time_, + _title_, + _tr_, + _track_, + _tt_, + _u_, + _ul_, + _var_, + _video_, + _wbr_, + + // litehtml internal tags + __tag_before_, // note: real tag cannot start with '-' + __tag_after_, + + // CSS pseudo-elements + _before_, + _after_, + + // CSS pseudo-classes + _root_, + _only_child_, + _only_of_type_, + _first_child_, + _first_of_type_, + _last_child_, + _last_of_type_, + _nth_child_, + _nth_of_type_, + _nth_last_child_, + _nth_last_of_type_, + _not_, + _lang_, + + _active_, + _hover_, + + // CSS property names + _background_, + _background_color_, + _background_image_, + _background_image_baseurl_, + _background_repeat_, + _background_origin_, + _background_clip_, + _background_attachment_, + _background_size_, + _background_position_, + _background_position_x_, + _background_position_y_, + + _border_, + _border_width_, + _border_style_, + _border_color_, + + _border_spacing_, + __litehtml_border_spacing_x_, + __litehtml_border_spacing_y_, + + _border_left_, + _border_right_, + _border_top_, + _border_bottom_, + + _border_left_style_, + _border_right_style_, + _border_top_style_, + _border_bottom_style_, + + _border_left_width_, + _border_right_width_, + _border_top_width_, + _border_bottom_width_, + + _border_left_color_, + _border_right_color_, + _border_top_color_, + _border_bottom_color_, + + _border_radius_, + _border_radius_x_, + _border_radius_y_, + + _border_bottom_left_radius_, + _border_bottom_left_radius_x_, + _border_bottom_left_radius_y_, + + _border_bottom_right_radius_, + _border_bottom_right_radius_x_, + _border_bottom_right_radius_y_, + + _border_top_left_radius_, + _border_top_left_radius_x_, + _border_top_left_radius_y_, + + _border_top_right_radius_, + _border_top_right_radius_x_, + _border_top_right_radius_y_, + + _list_style_, + _list_style_type_, + _list_style_position_, + _list_style_image_, + _list_style_image_baseurl_, + + _margin_, + _margin_left_, + _margin_right_, + _margin_top_, + _margin_bottom_, + _padding_, + _padding_left_, + _padding_right_, + _padding_top_, + _padding_bottom_, + + _font_, + _font_family_, + _font_style_, + _font_variant_, + _font_weight_, + _font_size_, + _line_height_, + _text_decoration_, + + _white_space_, + _text_align_, + _vertical_align_, + _color_, + _width_, + _height_, + _min_width_, + _min_height_, + _max_width_, + _max_height_, + _position_, + _overflow_, + _display_, + _visibility_, + _box_sizing_, + _z_index_, + _float_, + _clear_, + _text_indent_, + _left_, + _right_, + _top_, + _bottom_, + _cursor_, + _content_, + _border_collapse_, + _text_transform_, + + _flex_, + _flex_flow_, + _flex_direction_, + _flex_wrap_, + _justify_content_, + _align_items_, + _align_content_, + _align_self_, + _flex_grow_, + _flex_shrink_, + _flex_basis_, +); +#undef STRING_ID +extern const string_id empty_id; // _id("") +extern const string_id star_id; // _id("*") + +string_id _id(const string& str); +const string& _s(string_id id); + +} // namespace litehtml + +#endif // LH_STRING_ID_H diff --git a/src/plugins/litehtml_viewer/litehtml/strtod.cpp b/src/plugins/litehtml_viewer/litehtml/strtod.cpp @@ -0,0 +1,275 @@ +/* + * strtod.c -- + * + * Source code for the "strtod" library procedure. + * + * Copyright (c) 1988-1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + * + * RCS: @(#) $Id$ + */ + +#include "html.h" +#include <cstdlib> +#include <cctype> +#include <cerrno> + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif +#ifndef NULL +#define NULL 0 +#endif + +static int maxExponent = 511; /* Largest possible base 10 exponent. Any + * exponent larger than this will already + * produce underflow or overflow, so there's + * no need to worry about additional digits. + */ +static double powersOf10[] = { /* Table giving binary powers of 10. Entry */ + 10., /* is 10^2^i. Used to convert decimal */ + 100., /* exponents into floating-point numbers. */ + 1.0e4, + 1.0e8, + 1.0e16, + 1.0e32, + 1.0e64, + 1.0e128, + 1.0e256 +}; + +/* + *---------------------------------------------------------------------- + * + * strtod -- + * + * This procedure converts a floating-point number from an ASCII + * decimal representation to internal double-precision format. + * + * Results: + * The return value is the double-precision floating-point + * representation of the characters in string. If endPtr isn't + * NULL, then *endPtr is filled in with the address of the + * next character after the last one that was part of the + * floating-point number. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +double litehtml::t_strtod(const char* string, char** endPtr) +{ + int sign, expSign = FALSE; + double fraction, dblExp, *d; + const char *p; + int c; + int exp = 0; /* Exponent read from "EX" field. */ + int fracExp = 0; /* Exponent that derives from the fractional + * part. Under normal circumstatnces, it is + * the negative of the number of digits in F. + * However, if I is very long, the last digits + * of I get dropped (otherwise a long I with a + * large negative exponent could cause an + * unnecessary overflow on I alone). In this + * case, fracExp is incremented one for each + * dropped digit. */ + int mantSize; /* Number of digits in mantissa. */ + int decPt; /* Number of mantissa digits BEFORE decimal + * point. */ + const char *pExp; /* Temporarily holds location of exponent + * in string. */ + + /* + * Strip off leading blanks and check for a sign. + */ + + p = string; + while (isspace(*p)) + { + p += 1; + } + if (*p == '-') + { + sign = TRUE; + p += 1; + } else + { + if (*p == '+') + { + p += 1; + } + sign = FALSE; + } + + /* + * Count the number of digits in the mantissa (including the decimal + * point), and also locate the decimal point. + */ + + decPt = -1; + for (mantSize = 0; ; mantSize += 1) + { + c = *p; + if (!t_isdigit(c)) + { + if ((c != '.') || (decPt >= 0)) + { + break; + } + decPt = mantSize; + } + p += 1; + } + + /* + * Now suck up the digits in the mantissa. Use two integers to + * collect 9 digits each (this is faster than using floating-point). + * If the mantissa has more than 18 digits, ignore the extras, since + * they can't affect the value anyway. + */ + + pExp = p; + p -= mantSize; + if (decPt < 0) + { + decPt = mantSize; + } else + { + mantSize -= 1; /* One of the digits was the point. */ + } + if (mantSize > 18) + { + fracExp = decPt - 18; + mantSize = 18; + } else + { + fracExp = decPt - mantSize; + } + if (mantSize == 0) + { + fraction = 0.0; + p = string; + goto done; + } else + { + int frac1, frac2; + frac1 = 0; + for ( ; mantSize > 9; mantSize -= 1) + { + c = *p; + p += 1; + if (c == '.') + { + c = *p; + p += 1; + } + frac1 = 10*frac1 + (c - '0'); + } + frac2 = 0; + for (; mantSize > 0; mantSize -= 1) + { + c = *p; + p += 1; + if (c == '.') + { + c = *p; + p += 1; + } + frac2 = 10*frac2 + (c - '0'); + } + fraction = (1.0e9 * frac1) + frac2; + } + + /* + * Skim off the exponent. + */ + + p = pExp; + if ((*p == 'E') || (*p == 'e')) + { + p += 1; + if (*p == '-') + { + expSign = TRUE; + p += 1; + } else + { + if (*p == '+') + { + p += 1; + } + expSign = FALSE; + } + while (isdigit(*p)) + { + exp = exp * 10 + (*p - '0'); + p += 1; + } + } + if (expSign) + { + exp = fracExp - exp; + } else + { + exp = fracExp + exp; + } + + /* + * Generate a floating-point number that represents the exponent. + * Do this by processing the exponent one bit at a time to combine + * many powers of 2 of 10. Then combine the exponent with the + * fraction. + */ + + if (exp < 0) + { + expSign = TRUE; + exp = -exp; + } else + { + expSign = FALSE; + } + if (exp > maxExponent) + { + exp = maxExponent; + errno = ERANGE; + } + dblExp = 1.0; + for (d = powersOf10; exp != 0; exp >>= 1, d += 1) + { + if (exp & 01) + { + dblExp *= *d; + } + } + if (expSign) + { + fraction /= dblExp; + } else + { + fraction *= dblExp; + } + +done: + if (endPtr != nullptr) + { + *endPtr = (char *) p; + } + + if (sign) + { + return -fraction; + } + return fraction; +} diff --git a/src/plugins/litehtml_viewer/litehtml/style.cpp b/src/plugins/litehtml_viewer/litehtml/style.cpp @@ -1,48 +1,69 @@ #include "html.h" #include "style.h" -#include <functional> -#include <algorithm> -#ifndef WINCE -#include <locale> -#endif -litehtml::string_map litehtml::style::m_valid_values = +namespace litehtml { - { _t("white-space"), white_space_strings } -}; -litehtml::style::style() +std::map<string_id, string> style::m_valid_values = { -} + { _display_, style_display_strings }, + { _visibility_, visibility_strings }, + { _position_, element_position_strings }, + { _float_, element_float_strings }, + { _clear_, element_clear_strings }, + { _overflow_, overflow_strings }, + { _box_sizing_, box_sizing_strings }, -litehtml::style::style( const style& val ) -{ - m_properties = val.m_properties; -} + { _text_align_, text_align_strings }, + { _vertical_align_, vertical_align_strings }, + { _text_transform_, text_transform_strings }, + { _white_space_, white_space_strings }, -litehtml::style::~style() -{ + { _font_style_, font_style_strings }, + { _font_variant_, font_variant_strings }, + { _font_weight_, font_weight_strings }, -} + { _list_style_type_, list_style_type_strings }, + { _list_style_position_, list_style_position_strings }, -void litehtml::style::parse( const tchar_t* txt, const tchar_t* baseurl ) + { _border_left_style_, border_style_strings }, + { _border_right_style_, border_style_strings }, + { _border_top_style_, border_style_strings }, + { _border_bottom_style_, border_style_strings }, + { _border_collapse_, border_collapse_strings }, + + // these 4 properties are comma-separated lists of keywords, see parse_keyword_comma_list + { _background_attachment_, background_attachment_strings }, + { _background_repeat_, background_repeat_strings }, + { _background_clip_, background_box_strings }, + { _background_origin_, background_box_strings }, + + { _flex_direction_, flex_direction_strings }, + { _flex_wrap_, flex_wrap_strings }, + { _justify_content_, flex_justify_content_strings }, + { _align_items_, flex_align_items_strings }, + { _align_content_, flex_align_content_strings }, + { _align_self_, flex_align_self_strings }, +}; + +void style::parse(const string& txt, const string& baseurl, document_container* container) { - std::vector<tstring> properties; - split_string(txt, properties, _t(";"), _t(""), _t("\"'")); + std::vector<string> properties; + split_string(txt, properties, ";", "", "\"'"); - for(std::vector<tstring>::const_iterator i = properties.begin(); i != properties.end(); i++) + for(const auto & property : properties) { - parse_property(*i, baseurl); + parse_property(property, baseurl, container); } } -void litehtml::style::parse_property( const tstring& txt, const tchar_t* baseurl ) +void style::parse_property(const string& txt, const string& baseurl, document_container* container) { - tstring::size_type pos = txt.find_first_of(_t(":")); - if(pos != tstring::npos) + string::size_type pos = txt.find_first_of(':'); + if(pos != string::npos) { - tstring name = txt.substr(0, pos); - tstring val = txt.substr(pos + 1); + string name = txt.substr(0, pos); + string val = txt.substr(pos + 1); trim(name); lcase(name); trim(val); @@ -50,594 +71,1028 @@ void litehtml::style::parse_property( const tstring& txt, const tchar_t* baseurl if(!name.empty() && !val.empty()) { string_vector vals; - split_string(val, vals, _t("!")); + split_string(val, vals, "!"); if(vals.size() == 1) { - add_property(name.c_str(), val.c_str(), baseurl, false); + add_property(_id(name), val, baseurl, false, container); } else if(vals.size() > 1) { trim(vals[0]); lcase(vals[1]); - add_property(name.c_str(), vals[0].c_str(), baseurl, vals[1] == _t("important")); + add_property(_id(name), vals[0], baseurl, vals[1] == "important", container); } } } } -void litehtml::style::combine( const litehtml::style& src ) +void style::add_property(string_id name, const string& val, const string& baseurl, bool important, document_container* container) { - for(props_map::const_iterator i = src.m_properties.begin(); i != src.m_properties.end(); i++) - { - add_parsed_property(i->first.c_str(), i->second.m_value.c_str(), i->second.m_important); - } -} + if (val.find("var(") != -1) return add_parsed_property(name, property_value(val, important, prop_type_var)); + if (val == "inherit" && name != _font_) return add_parsed_property(name, property_value(important, prop_type_inherit)); -void litehtml::style::add_property( const tchar_t* name, const tchar_t* val, const tchar_t* baseurl, bool important ) -{ - if(!name || !val) - { - return; - } + int idx; + string url; + css_length len[4], length; - // Add baseurl for background image - if( !t_strcmp(name, _t("background-image"))) + switch (name) { - add_parsed_property(name, val, important); - if(baseurl) + // keyword-only properties + case _display_: + case _visibility_: + case _position_: + case _float_: + case _clear_: + case _box_sizing_: + case _overflow_: + + case _text_align_: + case _vertical_align_: + case _text_transform_: + case _white_space_: + + case _font_style_: + case _font_variant_: + case _font_weight_: + + case _list_style_type_: + case _list_style_position_: + + case _border_top_style_: + case _border_bottom_style_: + case _border_left_style_: + case _border_right_style_: + case _border_collapse_: + + case _flex_direction_: + case _flex_wrap_: + case _justify_content_: + case _align_items_: + case _align_content_: + case _align_self_: + + idx = value_index(val, m_valid_values[name]); + if (idx >= 0) { - add_parsed_property(_t("background-image-baseurl"), baseurl, important); + add_parsed_property(name, property_value(idx, important)); } - } else + break; + + // <length> + case _text_indent_: + case _padding_left_: + case _padding_right_: + case _padding_top_: + case _padding_bottom_: + length.fromString(val); + add_parsed_property(name, property_value(length, important)); + break; + + // <length> | auto + case _left_: + case _right_: + case _top_: + case _bottom_: + case _z_index_: // <integer> | auto + case _width_: + case _height_: + case _min_width_: + case _min_height_: + case _margin_left_: + case _margin_right_: + case _margin_top_: + case _margin_bottom_: + length.fromString(val, "auto", -1); + add_parsed_property(name, property_value(length, important)); + break; + + // <length> | none + case _max_width_: + case _max_height_: + length.fromString(val, "none", -1); + add_parsed_property(name, property_value(length, important)); + break; + + case _line_height_: + length.fromString(val, "normal", -1); + add_parsed_property(name, property_value(length, important)); + break; + + case _font_size_: + length.fromString(val, font_size_strings, -1); + add_parsed_property(name, property_value(length, important)); + break; + + // Parse background shorthand properties + case _background_: + parse_background(val, baseurl, important, container); + break; + + case _background_image_: + parse_background_image(val, baseurl, important); + break; + + case _background_attachment_: + case _background_repeat_: + case _background_clip_: + case _background_origin_: + parse_keyword_comma_list(name, val, important); + break; + + case _background_position_: + parse_background_position(val, important); + break; + + case _background_size_: + parse_background_size(val, important); + break; // Parse border spacing properties - if( !t_strcmp(name, _t("border-spacing"))) - { - string_vector tokens; - split_string(val, tokens, _t(" ")); - if(tokens.size() == 1) - { - add_parsed_property(_t("-litehtml-border-spacing-x"), tokens[0].c_str(), important); - add_parsed_property(_t("-litehtml-border-spacing-y"), tokens[0].c_str(), important); - } else if(tokens.size() == 2) - { - add_parsed_property(_t("-litehtml-border-spacing-x"), tokens[0].c_str(), important); - add_parsed_property(_t("-litehtml-border-spacing-y"), tokens[1].c_str(), important); - } - } else + case _border_spacing_: + parse_two_lengths(val, len); + add_parsed_property(__litehtml_border_spacing_x_, property_value(len[0], important)); + add_parsed_property(__litehtml_border_spacing_y_, property_value(len[1], important)); + break; // Parse borders shorthand properties - - if( !t_strcmp(name, _t("border"))) + case _border_: { string_vector tokens; - split_string(val, tokens, _t(" "), _t(""), _t("(")); - int idx; - tstring str; - for(string_vector::const_iterator tok = tokens.begin(); tok != tokens.end(); tok++) + split_string(val, tokens, " ", "", "("); + for (const auto& token : tokens) { - idx = value_index(tok->c_str(), border_style_strings, -1); - if(idx >= 0) + int idx = value_index(token, border_style_strings); + if (idx >= 0) { - add_property(_t("border-left-style"), tok->c_str(), baseurl, important); - add_property(_t("border-right-style"), tok->c_str(), baseurl, important); - add_property(_t("border-top-style"), tok->c_str(), baseurl, important); - add_property(_t("border-bottom-style"), tok->c_str(), baseurl, important); - } else + property_value style(idx, important); + add_parsed_property(_border_left_style_, style); + add_parsed_property(_border_right_style_, style); + add_parsed_property(_border_top_style_, style); + add_parsed_property(_border_bottom_style_, style); + } + else if (t_isdigit(token[0]) || token[0] == '.' || + value_in_list(token, border_width_strings)) { - if (t_isdigit((*tok)[0]) || (*tok)[0] == _t('.') || - value_in_list((*tok), _t("thin;medium;thick"))) - { - add_property(_t("border-left-width"), tok->c_str(), baseurl, important); - add_property(_t("border-right-width"), tok->c_str(), baseurl, important); - add_property(_t("border-top-width"), tok->c_str(), baseurl, important); - add_property(_t("border-bottom-width"), tok->c_str(), baseurl, important); - } - else - { - add_property(_t("border-left-color"), tok->c_str(), baseurl, important); - add_property(_t("border-right-color"), tok->c_str(), baseurl, important); - add_property(_t("border-top-color"), tok->c_str(), baseurl, important); - add_property(_t("border-bottom-color"), tok->c_str(), baseurl, important); - } + property_value width(parse_border_width(token), important); + add_parsed_property(_border_left_width_, width); + add_parsed_property(_border_right_width_, width); + add_parsed_property(_border_top_width_, width); + add_parsed_property(_border_bottom_width_, width); + } + else + { + web_color _color = web_color::from_string(token, container); + property_value color(_color, important); + add_parsed_property(_border_left_color_, color); + add_parsed_property(_border_right_color_, color); + add_parsed_property(_border_top_color_, color); + add_parsed_property(_border_bottom_color_, color); } } - } else if( !t_strcmp(name, _t("border-left")) || - !t_strcmp(name, _t("border-right")) || - !t_strcmp(name, _t("border-top")) || - !t_strcmp(name, _t("border-bottom")) ) + break; + } + + case _border_left_: + case _border_right_: + case _border_top_: + case _border_bottom_: { string_vector tokens; - split_string(val, tokens, _t(" "), _t(""), _t("(")); - int idx; - tstring str; - for(string_vector::const_iterator tok = tokens.begin(); tok != tokens.end(); tok++) + split_string(val, tokens, " ", "", "("); + for (const auto& token : tokens) { - idx = value_index(tok->c_str(), border_style_strings, -1); - if(idx >= 0) + int idx = value_index(token, border_style_strings); + if (idx >= 0) { - str = name; - str += _t("-style"); - add_property(str.c_str(), tok->c_str(), baseurl, important); - } else + add_parsed_property(_id(_s(name) + "-style"), property_value(idx, important)); + } + else if (t_isdigit(token[0]) || token[0] == '.' || + value_in_list(token, border_width_strings)) { - if(web_color::is_color(tok->c_str())) - { - str = name; - str += _t("-color"); - add_property(str.c_str(), tok->c_str(), baseurl, important); - } else - { - str = name; - str += _t("-width"); - add_property(str.c_str(), tok->c_str(), baseurl, important); - } + property_value width(parse_border_width(token), important); + add_parsed_property(_id(_s(name) + "-width"), width); + } + else + { + web_color color = web_color::from_string(token, container); + add_parsed_property(_id(_s(name) + "-color"), property_value(color, important)); } } - } else + break; + } - // Parse border radius shorthand properties - if(!t_strcmp(name, _t("border-bottom-left-radius"))) + // Parse border-width/style/color shorthand properties + case _border_width_: + case _border_style_: + case _border_color_: { - string_vector tokens; - split_string(val, tokens, _t(" ")); - if(tokens.size() >= 2) - { - add_property(_t("border-bottom-left-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-bottom-left-radius-y"), tokens[1].c_str(), baseurl, important); - } else if(tokens.size() == 1) - { - add_property(_t("border-bottom-left-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-bottom-left-radius-y"), tokens[0].c_str(), baseurl, important); - } + string prop = name == _border_width_ ? "-width" : name == _border_style_ ? "-style" : "-color"; - } else if(!t_strcmp(name, _t("border-bottom-right-radius"))) - { string_vector tokens; - split_string(val, tokens, _t(" ")); - if(tokens.size() >= 2) + split_string(val, tokens, " "); + if (tokens.size() == 4) { - add_property(_t("border-bottom-right-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-bottom-right-radius-y"), tokens[1].c_str(), baseurl, important); - } else if(tokens.size() == 1) + add_property(_id("border-top" + prop), tokens[0], baseurl, important, container); + add_property(_id("border-right" + prop), tokens[1], baseurl, important, container); + add_property(_id("border-bottom" + prop), tokens[2], baseurl, important, container); + add_property(_id("border-left" + prop), tokens[3], baseurl, important, container); + } + else if (tokens.size() == 3) { - add_property(_t("border-bottom-right-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-bottom-right-radius-y"), tokens[0].c_str(), baseurl, important); + add_property(_id("border-top" + prop), tokens[0], baseurl, important, container); + add_property(_id("border-right" + prop), tokens[1], baseurl, important, container); + add_property(_id("border-left" + prop), tokens[1], baseurl, important, container); + add_property(_id("border-bottom" + prop), tokens[2], baseurl, important, container); } - - } else if(!t_strcmp(name, _t("border-top-right-radius"))) - { - string_vector tokens; - split_string(val, tokens, _t(" ")); - if(tokens.size() >= 2) + else if (tokens.size() == 2) { - add_property(_t("border-top-right-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-top-right-radius-y"), tokens[1].c_str(), baseurl, important); - } else if(tokens.size() == 1) + add_property(_id("border-top" + prop), tokens[0], baseurl, important, container); + add_property(_id("border-bottom" + prop), tokens[0], baseurl, important, container); + add_property(_id("border-right" + prop), tokens[1], baseurl, important, container); + add_property(_id("border-left" + prop), tokens[1], baseurl, important, container); + } + else if (tokens.size() == 1) { - add_property(_t("border-top-right-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-top-right-radius-y"), tokens[0].c_str(), baseurl, important); + add_property(_id("border-top" + prop), tokens[0], baseurl, important, container); + add_property(_id("border-bottom" + prop), tokens[0], baseurl, important, container); + add_property(_id("border-right" + prop), tokens[0], baseurl, important, container); + add_property(_id("border-left" + prop), tokens[0], baseurl, important, container); } + break; + } + + case _border_top_width_: + case _border_bottom_width_: + case _border_left_width_: + case _border_right_width_: + length = parse_border_width(val); + add_parsed_property(name, property_value(length, important)); + break; - } else if(!t_strcmp(name, _t("border-top-left-radius"))) + case _color_: + case _background_color_: + case _border_top_color_: + case _border_bottom_color_: + case _border_left_color_: + case _border_right_color_: { - string_vector tokens; - split_string(val, tokens, _t(" ")); - if(tokens.size() >= 2) - { - add_property(_t("border-top-left-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-top-left-radius-y"), tokens[1].c_str(), baseurl, important); - } else if(tokens.size() == 1) - { - add_property(_t("border-top-left-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-top-left-radius-y"), tokens[0].c_str(), baseurl, important); - } + web_color color = web_color::from_string(val, container); + add_parsed_property(name, property_value(color, important)); + break; + } - } else + // Parse border radius shorthand properties + case _border_bottom_left_radius_: + case _border_bottom_right_radius_: + case _border_top_right_radius_: + case _border_top_left_radius_: + parse_two_lengths(val, len); + add_parsed_property(_id(_s(name) + "-x"), property_value(len[0], important)); + add_parsed_property(_id(_s(name) + "-y"), property_value(len[1], important)); + break; // Parse border-radius shorthand properties - if(!t_strcmp(name, _t("border-radius"))) + case _border_radius_: { string_vector tokens; - split_string(val, tokens, _t("/")); - if(tokens.size() == 1) + split_string(val, tokens, "/"); + if (tokens.size() == 1) { - add_property(_t("border-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-radius-y"), tokens[0].c_str(), baseurl, important); - } else if(tokens.size() >= 2) + add_property(_border_radius_x_, tokens[0], baseurl, important, container); + add_property(_border_radius_y_, tokens[0], baseurl, important, container); + } + else if (tokens.size() >= 2) { - add_property(_t("border-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-radius-y"), tokens[1].c_str(), baseurl, important); + add_property(_border_radius_x_, tokens[0], baseurl, important, container); + add_property(_border_radius_y_, tokens[1], baseurl, important, container); } - } else if(!t_strcmp(name, _t("border-radius-x"))) - { - string_vector tokens; - split_string(val, tokens, _t(" ")); - if(tokens.size() == 1) - { - add_property(_t("border-top-left-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-top-right-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-bottom-right-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-bottom-left-radius-x"), tokens[0].c_str(), baseurl, important); - } else if(tokens.size() == 2) - { - add_property(_t("border-top-left-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-top-right-radius-x"), tokens[1].c_str(), baseurl, important); - add_property(_t("border-bottom-right-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-bottom-left-radius-x"), tokens[1].c_str(), baseurl, important); - } else if(tokens.size() == 3) - { - add_property(_t("border-top-left-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-top-right-radius-x"), tokens[1].c_str(), baseurl, important); - add_property(_t("border-bottom-right-radius-x"), tokens[2].c_str(), baseurl, important); - add_property(_t("border-bottom-left-radius-x"), tokens[1].c_str(), baseurl, important); - } else if(tokens.size() == 4) - { - add_property(_t("border-top-left-radius-x"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-top-right-radius-x"), tokens[1].c_str(), baseurl, important); - add_property(_t("border-bottom-right-radius-x"), tokens[2].c_str(), baseurl, important); - add_property(_t("border-bottom-left-radius-x"), tokens[3].c_str(), baseurl, important); - } - } else if(!t_strcmp(name, _t("border-radius-y"))) + break; + } + case _border_radius_x_: + case _border_radius_y_: { - string_vector tokens; - split_string(val, tokens, _t(" ")); - if(tokens.size() == 1) - { - add_property(_t("border-top-left-radius-y"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-top-right-radius-y"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-bottom-right-radius-y"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-bottom-left-radius-y"), tokens[0].c_str(), baseurl, important); - } else if(tokens.size() == 2) + string_id top_left, top_right, bottom_right, bottom_left; + if (name == _border_radius_x_) { - add_property(_t("border-top-left-radius-y"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-top-right-radius-y"), tokens[1].c_str(), baseurl, important); - add_property(_t("border-bottom-right-radius-y"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-bottom-left-radius-y"), tokens[1].c_str(), baseurl, important); - } else if(tokens.size() == 3) + top_left = _border_top_left_radius_x_; + top_right = _border_top_right_radius_x_; + bottom_right = _border_bottom_right_radius_x_; + bottom_left = _border_bottom_left_radius_x_; + } + else { - add_property(_t("border-top-left-radius-y"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-top-right-radius-y"), tokens[1].c_str(), baseurl, important); - add_property(_t("border-bottom-right-radius-y"), tokens[2].c_str(), baseurl, important); - add_property(_t("border-bottom-left-radius-y"), tokens[1].c_str(), baseurl, important); - } else if(tokens.size() == 4) + top_left = _border_top_left_radius_y_; + top_right = _border_top_right_radius_y_; + bottom_right = _border_bottom_right_radius_y_; + bottom_left = _border_bottom_left_radius_y_; + } + + switch (parse_four_lengths(val, len)) { - add_property(_t("border-top-left-radius-y"), tokens[0].c_str(), baseurl, important); - add_property(_t("border-top-right-radius-y"), tokens[1].c_str(), baseurl, important); - add_property(_t("border-bottom-right-radius-y"), tokens[2].c_str(), baseurl, important); - add_property(_t("border-bottom-left-radius-y"), tokens[3].c_str(), baseurl, important); + case 1: + add_parsed_property(top_left, property_value(len[0], important)); + add_parsed_property(top_right, property_value(len[0], important)); + add_parsed_property(bottom_right, property_value(len[0], important)); + add_parsed_property(bottom_left, property_value(len[0], important)); + break; + case 2: + add_parsed_property(top_left, property_value(len[0], important)); + add_parsed_property(top_right, property_value(len[1], important)); + add_parsed_property(bottom_right, property_value(len[0], important)); + add_parsed_property(bottom_left, property_value(len[1], important)); + break; + case 3: + add_parsed_property(top_left, property_value(len[0], important)); + add_parsed_property(top_right, property_value(len[1], important)); + add_parsed_property(bottom_right, property_value(len[2], important)); + add_parsed_property(bottom_left, property_value(len[1], important)); + break; + case 4: + add_parsed_property(top_left, property_value(len[0], important)); + add_parsed_property(top_right, property_value(len[1], important)); + add_parsed_property(bottom_right, property_value(len[2], important)); + add_parsed_property(bottom_left, property_value(len[3], important)); + break; } + break; } - else // Parse list-style shorthand properties - if(!t_strcmp(name, _t("list-style"))) + case _list_style_: { - add_parsed_property(_t("list-style-type"), _t("disc"), important); - add_parsed_property(_t("list-style-position"), _t("outside"), important); - add_parsed_property(_t("list-style-image"), _t(""), important); - add_parsed_property(_t("list-style-image-baseurl"), _t(""), important); + add_parsed_property(_list_style_type_, property_value(list_style_type_disc, important)); + add_parsed_property(_list_style_position_, property_value(list_style_position_outside, important)); + add_parsed_property(_list_style_image_, property_value("", important)); + add_parsed_property(_list_style_image_baseurl_, property_value("", important)); string_vector tokens; - split_string(val, tokens, _t(" "), _t(""), _t("(")); - for(string_vector::iterator tok = tokens.begin(); tok != tokens.end(); tok++) + split_string(val, tokens, " ", "", "("); + for (const auto& token : tokens) { - int idx = value_index(tok->c_str(), list_style_type_strings, -1); - if(idx >= 0) + int idx = value_index(token, list_style_type_strings); + if (idx >= 0) { - add_parsed_property(_t("list-style-type"), *tok, important); - } else + add_parsed_property(_list_style_type_, property_value(idx, important)); + } + else { - idx = value_index(tok->c_str(), list_style_position_strings, -1); - if(idx >= 0) + idx = value_index(token, list_style_position_strings); + if (idx >= 0) { - add_parsed_property(_t("list-style-position"), *tok, important); - } else if(!t_strncmp(val, _t("url"), 3)) + add_parsed_property(_list_style_position_, property_value(idx, important)); + } + else if (!strncmp(token.c_str(), "url", 3)) { - add_parsed_property(_t("list-style-image"), *tok, important); - if(baseurl) - { - add_parsed_property(_t("list-style-image-baseurl"), baseurl, important); - } + css::parse_css_url(token, url); + add_parsed_property(_list_style_image_, property_value(url, important)); + add_parsed_property(_list_style_image_baseurl_, property_value(baseurl, important)); } } } - } else + break; + } - // Add baseurl for background image - if( !t_strcmp(name, _t("list-style-image"))) + case _list_style_image_: + css::parse_css_url(val, url); + add_parsed_property(_list_style_image_, property_value(url, important)); + add_parsed_property(_list_style_image_baseurl_, property_value(baseurl, important)); + break; + + // Parse margin and padding shorthand properties + case _margin_: + case _padding_: { - add_parsed_property(name, val, important); - if(baseurl) + switch (parse_four_lengths(val, len)) { - add_parsed_property(_t("list-style-image-baseurl"), baseurl, important); + case 4: + add_parsed_property(_id(_s(name) + "-top"), property_value(len[0], important)); + add_parsed_property(_id(_s(name) + "-right"), property_value(len[1], important)); + add_parsed_property(_id(_s(name) + "-bottom"), property_value(len[2], important)); + add_parsed_property(_id(_s(name) + "-left"), property_value(len[3], important)); + break; + case 3: + add_parsed_property(_id(_s(name) + "-top"), property_value(len[0], important)); + add_parsed_property(_id(_s(name) + "-right"), property_value(len[1], important)); + add_parsed_property(_id(_s(name) + "-left"), property_value(len[1], important)); + add_parsed_property(_id(_s(name) + "-bottom"), property_value(len[2], important)); + break; + case 2: + add_parsed_property(_id(_s(name) + "-top"), property_value(len[0], important)); + add_parsed_property(_id(_s(name) + "-bottom"), property_value(len[0], important)); + add_parsed_property(_id(_s(name) + "-right"), property_value(len[1], important)); + add_parsed_property(_id(_s(name) + "-left"), property_value(len[1], important)); + break; + case 1: + add_parsed_property(_id(_s(name) + "-top"), property_value(len[0], important)); + add_parsed_property(_id(_s(name) + "-bottom"), property_value(len[0], important)); + add_parsed_property(_id(_s(name) + "-right"), property_value(len[0], important)); + add_parsed_property(_id(_s(name) + "-left"), property_value(len[0], important)); + break; } - } else - - // Parse background shorthand properties - if(!t_strcmp(name, _t("background"))) - { - parse_short_background(val, baseurl, important); + break; + } - } else - - // Parse margin and padding shorthand properties - if(!t_strcmp(name, _t("margin")) || !t_strcmp(name, _t("padding"))) + // Parse font shorthand properties + case _font_: + parse_font(val, important); + break; + + // Parse flex-flow shorthand properties + case _flex_flow_: { string_vector tokens; - split_string(val, tokens, _t(" ")); - if(tokens.size() >= 4) - { - add_parsed_property(tstring(name) + _t("-top"), tokens[0], important); - add_parsed_property(tstring(name) + _t("-right"), tokens[1], important); - add_parsed_property(tstring(name) + _t("-bottom"), tokens[2], important); - add_parsed_property(tstring(name) + _t("-left"), tokens[3], important); - } else if(tokens.size() == 3) - { - add_parsed_property(tstring(name) + _t("-top"), tokens[0], important); - add_parsed_property(tstring(name) + _t("-right"), tokens[1], important); - add_parsed_property(tstring(name) + _t("-left"), tokens[1], important); - add_parsed_property(tstring(name) + _t("-bottom"), tokens[2], important); - } else if(tokens.size() == 2) - { - add_parsed_property(tstring(name) + _t("-top"), tokens[0], important); - add_parsed_property(tstring(name) + _t("-bottom"), tokens[0], important); - add_parsed_property(tstring(name) + _t("-right"), tokens[1], important); - add_parsed_property(tstring(name) + _t("-left"), tokens[1], important); - } else if(tokens.size() == 1) - { - add_parsed_property(tstring(name) + _t("-top"), tokens[0], important); - add_parsed_property(tstring(name) + _t("-bottom"), tokens[0], important); - add_parsed_property(tstring(name) + _t("-right"), tokens[0], important); - add_parsed_property(tstring(name) + _t("-left"), tokens[0], important); - } - } else - - - // Parse border-* shorthand properties - if( !t_strcmp(name, _t("border-left")) || - !t_strcmp(name, _t("border-right")) || - !t_strcmp(name, _t("border-top")) || - !t_strcmp(name, _t("border-bottom"))) - { - parse_short_border(name, val, important); - } else - - // Parse border-width/style/color shorthand properties - if( !t_strcmp(name, _t("border-width")) || - !t_strcmp(name, _t("border-style")) || - !t_strcmp(name, _t("border-color")) ) + split_string(val, tokens, " "); + for (const auto& tok : tokens) + { + int idx; + if ((idx = value_index(tok, flex_direction_strings)) >= 0) + { + add_parsed_property(_flex_direction_, property_value(idx, important)); + } + else if ((idx = value_index(tok, flex_wrap_strings)) >= 0) + { + add_parsed_property(_flex_wrap_, property_value(idx, important)); + } + } + break; + } + + // Parse flex shorthand properties + case _flex_: + parse_flex(val, important); + break; + + case _flex_grow_: + case _flex_shrink_: + add_parsed_property(name, property_value(t_strtof(val), important)); + break; + + case _flex_basis_: + length.fromString(val, flex_basis_strings, -1); + add_parsed_property(_flex_basis_, property_value(length, important)); + break; + + default: + add_parsed_property(name, property_value(val, important)); + } +} + +css_length style::parse_border_width(const string& str) +{ + css_length len; + if (t_isdigit(str[0]) || str[0] == '.') { - string_vector nametokens; - split_string(name, nametokens, _t("-")); + len.fromString(str); + } + else + { + int idx = value_index(str, border_width_strings); + if (idx >= 0) + { + len.set_value(border_width_values[idx], css_units_px); + } + } + return len; +} - string_vector tokens; - split_string(val, tokens, _t(" ")); - if(tokens.size() >= 4) - { - add_parsed_property(nametokens[0] + _t("-top-") + nametokens[1], tokens[0], important); - add_parsed_property(nametokens[0] + _t("-right-") + nametokens[1], tokens[1], important); - add_parsed_property(nametokens[0] + _t("-bottom-") + nametokens[1], tokens[2], important); - add_parsed_property(nametokens[0] + _t("-left-") + nametokens[1], tokens[3], important); - } else if(tokens.size() == 3) - { - add_parsed_property(nametokens[0] + _t("-top-") + nametokens[1], tokens[0], important); - add_parsed_property(nametokens[0] + _t("-right-") + nametokens[1], tokens[1], important); - add_parsed_property(nametokens[0] + _t("-left-") + nametokens[1], tokens[1], important); - add_parsed_property(nametokens[0] + _t("-bottom-") + nametokens[1], tokens[2], important); - } else if(tokens.size() == 2) - { - add_parsed_property(nametokens[0] + _t("-top-") + nametokens[1], tokens[0], important); - add_parsed_property(nametokens[0] + _t("-bottom-") + nametokens[1], tokens[0], important); - add_parsed_property(nametokens[0] + _t("-right-") + nametokens[1], tokens[1], important); - add_parsed_property(nametokens[0] + _t("-left-") + nametokens[1], tokens[1], important); - } else if(tokens.size() == 1) - { - add_parsed_property(nametokens[0] + _t("-top-") + nametokens[1], tokens[0], important); - add_parsed_property(nametokens[0] + _t("-bottom-") + nametokens[1], tokens[0], important); - add_parsed_property(nametokens[0] + _t("-right-") + nametokens[1], tokens[0], important); - add_parsed_property(nametokens[0] + _t("-left-") + nametokens[1], tokens[0], important); - } - } else - - // Parse font shorthand properties - if(!t_strcmp(name, _t("font"))) +void style::parse_two_lengths(const string& str, css_length len[2]) +{ + string_vector tokens; + split_string(str, tokens, " "); + if (tokens.size() == 1) { - parse_short_font(val, important); - } else + css_length length; + length.fromString(tokens[0]); + len[0] = len[1] = length; + } + else if (tokens.size() == 2) { - add_parsed_property(name, val, important); + len[0].fromString(tokens[0]); + len[1].fromString(tokens[1]); } } -void litehtml::style::parse_short_border( const tstring& prefix, const tstring& val, bool important ) +int style::parse_four_lengths(const string& str, css_length len[4]) { string_vector tokens; - split_string(val, tokens, _t(" "), _t(""), _t("(")); - if(tokens.size() >= 3) + split_string(str, tokens, " "); + if (tokens.size() == 0 || tokens.size() > 4) { - add_parsed_property(prefix + _t("-width"), tokens[0], important); - add_parsed_property(prefix + _t("-style"), tokens[1], important); - add_parsed_property(prefix + _t("-color"), tokens[2], important); - } else if(tokens.size() == 2) + return 0; + } + for (size_t i = 0; i < tokens.size(); i++) { - if(iswdigit(tokens[0][0]) || value_index(val.c_str(), border_width_strings) >= 0) - { - add_parsed_property(prefix + _t("-width"), tokens[0], important); - add_parsed_property(prefix + _t("-style"), tokens[1], important); - } else - { - add_parsed_property(prefix + _t("-style"), tokens[0], important); - add_parsed_property(prefix + _t("-color"), tokens[1], important); - } + len[i].fromString(tokens[i]); } + return (int)tokens.size(); } -void litehtml::style::parse_short_background( const tstring& val, const tchar_t* baseurl, bool important ) +void style::parse_background(const string& val, const string& baseurl, bool important, document_container* container) { - add_parsed_property(_t("background-color"), _t("transparent"), important); - add_parsed_property(_t("background-image"), _t(""), important); - add_parsed_property(_t("background-image-baseurl"), _t(""), important); - add_parsed_property(_t("background-repeat"), _t("repeat"), important); - add_parsed_property(_t("background-origin"), _t("padding-box"), important); - add_parsed_property(_t("background-clip"), _t("border-box"), important); - add_parsed_property(_t("background-attachment"), _t("scroll"), important); + string_vector tokens; + split_string(val, tokens, ",", "", "("); + if (tokens.empty()) return; + + web_color color; + string_vector images; + int_vector repeats, origins, clips, attachments; + length_vector x_positions, y_positions; + size_vector sizes; - if(val == _t("none")) + for (const auto& token : tokens) { - return; + background bg; + if (!parse_one_background(token, container, bg)) + return; + + color = bg.m_color; + images.push_back(bg.m_image[0]); + repeats.push_back(bg.m_repeat[0]); + origins.push_back(bg.m_origin[0]); + clips.push_back(bg.m_clip[0]); + attachments.push_back(bg.m_attachment[0]); + x_positions.push_back(bg.m_position_x[0]); + y_positions.push_back(bg.m_position_y[0]); + sizes.push_back(bg.m_size[0]); + } + + add_parsed_property(_background_color_, property_value(color, important)); + add_parsed_property(_background_image_, property_value(images, important)); + add_parsed_property(_background_image_baseurl_, property_value(baseurl, important)); + add_parsed_property(_background_repeat_, property_value(repeats, important)); + add_parsed_property(_background_origin_, property_value(origins, important)); + add_parsed_property(_background_clip_, property_value(clips, important)); + add_parsed_property(_background_attachment_, property_value(attachments, important)); + add_parsed_property(_background_position_x_, property_value(x_positions, important)); + add_parsed_property(_background_position_y_, property_value(y_positions, important)); + add_parsed_property(_background_size_, property_value(sizes, important)); +} + +bool style::parse_one_background(const string& val, document_container* container, background& bg) +{ + bg.m_color = web_color::transparent; + bg.m_image = {""}; + bg.m_repeat = { background_repeat_repeat }; + bg.m_origin = { background_box_padding }; + bg.m_clip = { background_box_border }; + bg.m_attachment = { background_attachment_scroll }; + bg.m_position_x = { css_length(0, css_units_percentage) }; + bg.m_position_y = { css_length(0, css_units_percentage) }; + bg.m_size = { css_size(css_length::predef_value(background_size_auto), css_length::predef_value(background_size_auto)) }; + + if(val == "none") + { + return true; } string_vector tokens; - split_string(val, tokens, _t(" "), _t(""), _t("(")); + split_string(val, tokens, " \t\n\r", "", "("); + + bool color_found = false; + bool image_found = false; bool origin_found = false; - for(string_vector::iterator tok = tokens.begin(); tok != tokens.end(); tok++) + bool clip_found = false; + bool repeat_found = false; + bool attachment_found = false; + + string position; + for(const auto& token : tokens) { - if(tok->substr(0, 3) == _t("url")) + int idx; + if(token.substr(0, 3) == "url") { - add_parsed_property(_t("background-image"), *tok, important); - if(baseurl) - { - add_parsed_property(_t("background-image-baseurl"), baseurl, important); - } - - } else if( value_in_list(tok->c_str(), background_repeat_strings) ) + if (image_found) return false; + string url; + css::parse_css_url(token, url); + bg.m_image = { url }; + image_found = true; + } else if( (idx = value_index(token, background_repeat_strings)) >= 0 ) { - add_parsed_property(_t("background-repeat"), *tok, important); - } else if( value_in_list(tok->c_str(), background_attachment_strings) ) + if (repeat_found) return false; + bg.m_repeat = { idx }; + repeat_found = true; + } else if( (idx = value_index(token, background_attachment_strings)) >= 0 ) { - add_parsed_property(_t("background-attachment"), *tok, important); - } else if( value_in_list(tok->c_str(), background_box_strings) ) + if (attachment_found) return false; + bg.m_attachment = { idx }; + attachment_found = true; + } else if( (idx = value_index(token, background_box_strings)) >= 0 ) { if(!origin_found) { - add_parsed_property(_t("background-origin"), *tok, important); + bg.m_origin = { idx }; origin_found = true; } else { - add_parsed_property(_t("background-clip"),*tok, important); + if (clip_found) return false; + bg.m_clip = { idx }; + clip_found = true; } - } else if( value_in_list(tok->c_str(), _t("left;right;top;bottom;center")) || - iswdigit((*tok)[0]) || - (*tok)[0] == _t('-') || - (*tok)[0] == _t('.') || - (*tok)[0] == _t('+')) + } else if( value_in_list(token, background_position_strings) || + token.find('/') != -1 || + t_isdigit(token[0]) || + token[0] == '+' || + token[0] == '-' || + token[0] == '.' ) { - if(m_properties.find(_t("background-position")) != m_properties.end()) - { - m_properties[_t("background-position")].m_value = m_properties[_t("background-position")].m_value + _t(" ") + *tok; - } else - { - add_parsed_property(_t("background-position"), *tok, important); - } - } else if (web_color::is_color(tok->c_str())) + position += " " + token; + } else if (web_color::is_color(token, container)) + { + if (color_found) return false; + bg.m_color = web_color::from_string(token, container); + color_found = true; + } + else { - add_parsed_property(_t("background-color"), *tok, important); + return false; } } + + if (position != "") + { + string_vector tokens; + split_string(position, tokens, "/"); + + if (tokens.size() > 2) return false; + + if (tokens.size() == 2 && !parse_one_background_size(tokens[1], bg.m_size[0])) + return false; + + if (tokens.size() > 0 && !parse_one_background_position(tokens[0], bg.m_position_x[0], bg.m_position_y[0])) + return false; + } + + return true; +} + +void style::parse_background_image(const string& val, const string& baseurl, bool important) +{ + string_vector tokens; + split_string(val, tokens, ",", "", "("); + if (tokens.empty()) return; + + string_vector images; + + for (const auto& token : tokens) + { + string url; + css::parse_css_url(token, url); + images.push_back(url); + } + + add_parsed_property(_background_image_, property_value(images, important)); + add_parsed_property(_background_image_baseurl_, property_value(baseurl, important)); +} + +void style::parse_keyword_comma_list(string_id name, const string& val, bool important) +{ + string_vector tokens; + split_string(val, tokens, ","); + if (tokens.empty()) return; + + int_vector vec; + + for (auto& token : tokens) + { + trim(token); + int idx = value_index(token, m_valid_values[name]); + if (idx == -1) return; + vec.push_back(idx); + } + + add_parsed_property(name, property_value(vec, important)); +} + +void style::parse_background_position(const string& val, bool important) +{ + string_vector tokens; + split_string(val, tokens, ","); + if (tokens.empty()) return; + + length_vector x_positions, y_positions; + + for (const auto& token : tokens) + { + css_length x, y; + if(!parse_one_background_position(token, x, y)) return; + x_positions.push_back(x); + y_positions.push_back(y); + } + + add_parsed_property(_background_position_x_, property_value(x_positions, important)); + add_parsed_property(_background_position_y_, property_value(y_positions, important)); +} + +bool style::parse_one_background_position(const string& val, css_length& x, css_length& y) +{ + string_vector pos; + split_string(val, pos, " \t"); + + if (pos.empty() || pos.size() > 2) + { + return false; + } + + if (pos.size() == 1) + { + if (value_in_list(pos[0], "left;right;center")) + { + x.fromString(pos[0], "left;right;center"); + y.set_value(50, css_units_percentage); + } + else if (value_in_list(pos[0], "top;bottom;center")) + { + y.fromString(pos[0], "top;bottom;center"); + x.set_value(50, css_units_percentage); + } + else + { + x.fromString(pos[0], "left;right;center"); + y.set_value(50, css_units_percentage); + } + } + else if (pos.size() == 2) + { + if (value_in_list(pos[0], "left;right")) + { + x.fromString(pos[0], "left;right;center"); + y.fromString(pos[1], "top;bottom;center"); + } + else if (value_in_list(pos[0], "top;bottom")) + { + x.fromString(pos[1], "left;right;center"); + y.fromString(pos[0], "top;bottom;center"); + } + else if (value_in_list(pos[1], "left;right")) + { + x.fromString(pos[1], "left;right;center"); + y.fromString(pos[0], "top;bottom;center"); + } + else if (value_in_list(pos[1], "top;bottom")) + { + x.fromString(pos[0], "left;right;center"); + y.fromString(pos[1], "top;bottom;center"); + } + else + { + x.fromString(pos[0], "left;right;center"); + y.fromString(pos[1], "top;bottom;center"); + } + } + + if (x.is_predefined()) + { + switch (x.predef()) + { + case 0: + x.set_value(0, css_units_percentage); + break; + case 1: + x.set_value(100, css_units_percentage); + break; + case 2: + x.set_value(50, css_units_percentage); + break; + } + } + if (y.is_predefined()) + { + switch (y.predef()) + { + case 0: + y.set_value(0, css_units_percentage); + break; + case 1: + y.set_value(100, css_units_percentage); + break; + case 2: + y.set_value(50, css_units_percentage); + break; + } + } + return true; +} + +void style::parse_background_size(const string& val, bool important) +{ + string_vector tokens; + split_string(val, tokens, ","); + if (tokens.empty()) return; + + size_vector sizes; + + for (const auto& token : tokens) + { + css_size size; + if (!parse_one_background_size(token, size)) return; + sizes.push_back(size); + } + + add_parsed_property(_background_size_, property_value(sizes, important)); +} + +bool style::parse_one_background_size(const string& val, css_size& size) +{ + string_vector res; + split_string(val, res, " \t"); + if (res.empty()) + { + return false; + } + + size.width.fromString(res[0], background_size_strings); + if (res.size() > 1) + { + size.height.fromString(res[1], background_size_strings); + } + else + { + size.height.predef(background_size_auto); + } + return true; } -void litehtml::style::parse_short_font( const tstring& val, bool important ) +void style::parse_font(const string& val, bool important) { - add_parsed_property(_t("font-style"), _t("normal"), important); - add_parsed_property(_t("font-variant"), _t("normal"), important); - add_parsed_property(_t("font-weight"), _t("normal"), important); - add_parsed_property(_t("font-size"), _t("medium"), important); - add_parsed_property(_t("line-height"), _t("normal"), important); + if (val == "inherit") + { + add_parsed_property(_font_style_, property_value(important, prop_type_inherit)); + add_parsed_property(_font_variant_, property_value(important, prop_type_inherit)); + add_parsed_property(_font_weight_, property_value(important, prop_type_inherit)); + add_parsed_property(_font_size_, property_value(important, prop_type_inherit)); + add_parsed_property(_line_height_, property_value(important, prop_type_inherit)); + return; + } else + { + add_parsed_property(_font_style_, property_value(font_style_normal, important)); + add_parsed_property(_font_variant_, property_value(font_variant_normal, important)); + add_parsed_property(_font_weight_, property_value(font_weight_normal, important)); + add_parsed_property(_font_size_, property_value(font_size_medium, important)); + add_parsed_property(_line_height_, property_value(line_height_normal, important)); + } string_vector tokens; - split_string(val, tokens, _t(" "), _t(""), _t("\"")); + split_string(val, tokens, " ", "", "\""); - int idx = 0; - bool was_normal = false; + int idx; bool is_family = false; - tstring font_family; - for(string_vector::iterator tok = tokens.begin(); tok != tokens.end(); tok++) + string font_family; + for(const auto& token : tokens) { - idx = value_index(tok->c_str(), font_style_strings); - if(!is_family) + if(is_family) { - if(idx >= 0) + font_family += token; + continue; + } + + if((idx = value_index(token, font_style_strings)) >= 0) + { + if(idx == 0) { - if(idx == 0 && !was_normal) - { - add_parsed_property(_t("font-weight"), *tok, important); - add_parsed_property(_t("font-variant"), *tok, important); - add_parsed_property(_t("font-style"), *tok, important); - } else - { - add_parsed_property(_t("font-style"), *tok, important); - } + add_parsed_property(_font_style_, property_value(font_style_normal, important)); + add_parsed_property(_font_variant_, property_value(font_variant_normal, important)); + add_parsed_property(_font_weight_, property_value(font_weight_normal, important)); } else { - if(value_in_list(tok->c_str(), font_weight_strings)) - { - add_parsed_property(_t("font-weight"), *tok, important); - } else - { - if(value_in_list(tok->c_str(), font_variant_strings)) - { - add_parsed_property(_t("font-variant"), *tok, important); - } else if( iswdigit((*tok)[0]) ) - { - string_vector szlh; - split_string(*tok, szlh, _t("/")); - - if(szlh.size() == 1) - { - add_parsed_property(_t("font-size"), szlh[0], important); - } else if(szlh.size() >= 2) - { - add_parsed_property(_t("font-size"), szlh[0], important); - add_parsed_property(_t("line-height"), szlh[1], important); - } - } else - { - is_family = true; - font_family += *tok; - } - } + add_parsed_property(_font_style_, property_value(idx, important)); + } + } else if((idx = value_index(token, font_weight_strings)) >= 0) + { + add_parsed_property(_font_weight_, property_value(idx, important)); + } else if((idx = value_index(token, font_variant_strings)) >= 0) + { + add_parsed_property(_font_variant_, property_value(idx, important)); + } + else if(t_isdigit(token[0]) || token[0] == '.' || + value_in_list(token, font_size_strings) || token.find('/') != -1) + { + 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) + { + auto height = css_length::from_string(szlh[1], "normal", -1); + add_parsed_property(_line_height_, property_value(height, important)); } } else { - font_family += *tok; + is_family = true; + font_family += token; } } - add_parsed_property(_t("font-family"), font_family, important); + add_parsed_property(_font_family_, property_value(font_family, important)); } -void litehtml::style::add_parsed_property( const tstring& name, const tstring& val, bool important ) +void style::parse_flex(const string& val, bool important) { - bool is_valid = true; - string_map::iterator vals = m_valid_values.find(name); - if (vals != m_valid_values.end()) + auto is_number = [](const string& val) { - if (!value_in_list(val, vals->second)) + for (auto ch : val) { - is_valid = false; + if ((ch < '0' || ch > '9') && ch != '.') + { + return false; + } } - } + return true; + }; - if (is_valid) + css_length _auto = css_length::predef_value(flex_basis_auto); + + if (val == "initial") { - props_map::iterator prop = m_properties.find(name); - if (prop != m_properties.end()) + // 0 1 auto + add_parsed_property(_flex_grow_, property_value(0.f, important)); + add_parsed_property(_flex_shrink_, property_value(1.f, important)); + add_parsed_property(_flex_basis_, property_value(_auto, important)); + } + else if (val == "auto") + { + // 1 1 auto + 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(_auto, important)); + } + else if (val == "none") + { + // 0 0 auto + add_parsed_property(_flex_grow_, property_value(0.f, important)); + add_parsed_property(_flex_shrink_, property_value(0.f, important)); + add_parsed_property(_flex_basis_, property_value(_auto, important)); + } + else + { + string_vector tokens; + split_string(val, tokens, " "); + if (tokens.size() == 3) { - if (!prop->second.m_important || (important && prop->second.m_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); + + add_parsed_property(_flex_grow_, property_value(grow, important)); + add_parsed_property(_flex_shrink_, property_value(shrink, important)); + add_parsed_property(_flex_basis_, property_value(basis, important)); + } + else if (tokens.size() == 2) + { + float grow = t_strtof(tokens[0]); + add_parsed_property(_flex_grow_, property_value(grow, important)); + + if (is_number(tokens[1])) { - prop->second.m_value = val; - prop->second.m_important = important; + float shrink = t_strtof(tokens[1]); + add_parsed_property(_flex_shrink_, property_value(shrink, important)); + } + else + { + auto basis = css_length::from_string(tokens[1], flex_basis_strings, -1); + add_parsed_property(_flex_basis_, property_value(basis, important)); } } - else + else if (tokens.size() == 1) { - m_properties[name] = property_value(val.c_str(), important); + if (is_number(tokens[0])) + { + 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)); + } + } + else + { + auto basis = css_length::from_string(tokens[0], flex_basis_strings, -1); + add_parsed_property(_flex_basis_, property_value(basis, important)); + } } } } -void litehtml::style::remove_property( const tstring& name, bool important ) +void style::add_parsed_property( string_id name, const property_value& propval ) { - props_map::iterator prop = m_properties.find(name); + auto prop = m_properties.find(name); + if (prop != m_properties.end()) + { + if (!prop->second.m_important || (propval.m_important && prop->second.m_important)) + { + prop->second = propval; + } + } + else + { + m_properties[name] = propval; + } +} + +void style::remove_property( string_id name, bool important ) +{ + auto prop = m_properties.find(name); if(prop != m_properties.end()) { if( !prop->second.m_important || (important && prop->second.m_important) ) @@ -646,3 +1101,55 @@ void litehtml::style::remove_property( const tstring& name, bool important ) } } } + +void style::combine(const style& src) +{ + for (const auto& property : src.m_properties) + { + add_parsed_property(property.first, property.second); + } +} + +const property_value& style::get_property(string_id name) const +{ + auto it = m_properties.find(name); + if (it != m_properties.end()) + { + return it->second; + } + static property_value dummy; + return dummy; +} + +void style::subst_vars_(string& str, const element* el) +{ + while (1) + { + auto start = str.find("var("); + if (start == -1) break; + if (start > 0 && isalnum(str[start - 1])) break; + auto end = str.find(")", start + 4); + if (end == -1) break; + auto name = str.substr(start + 4, end - start - 4); + trim(name); + string val = el->get_custom_property(_id(name), ""); + str.replace(start, end - start + 1, val); + } +} + +void style::subst_vars(const element* el) +{ + for (auto& prop : m_properties) + { + if (prop.second.m_type == prop_type_var) + { + subst_vars_(prop.second.m_string, el); + // re-adding the same property + // if it is a custom property it will be readded as a string (currently it is prop_type_var) + // if it is a standard css property it will be parsed and properly added as typed property + add_property(prop.first, prop.second.m_string, "", prop.second.m_important, el->get_document()->container()); + } + } +} + +} // namespace litehtml +\ No newline at end of file diff --git a/src/plugins/litehtml_viewer/litehtml/style.h b/src/plugins/litehtml_viewer/litehtml/style.h @@ -1,41 +1,163 @@ #ifndef LH_STYLE_H #define LH_STYLE_H -#include "attributes.h" -#include <string> - namespace litehtml { + enum property_type + { + prop_type_invalid, // indicates "not found" condition in style::get_property + prop_type_inherit, // "inherit" was specified as the value of this property + + prop_type_enum_item, + prop_type_enum_item_vector, + prop_type_length, + prop_type_length_vector, + prop_type_number, + prop_type_color, + prop_type_string, + prop_type_string_vector, + prop_type_size_vector, + + prop_type_var, // also string, but needs further parsing because of var() + }; + class property_value { public: - tstring m_value; + property_type m_type; bool m_important; + union { + int m_enum_item; + int_vector m_enum_item_vector; + css_length m_length; + length_vector m_length_vector; + float m_number; + web_color m_color; + string m_string; + string_vector m_string_vector; + size_vector m_size_vector; + }; + property_value() + : m_type(prop_type_invalid) { - m_important = false; } - property_value(const tchar_t* val, bool imp) + property_value(bool important, property_type type) + : m_type(type), m_important(important) { - m_important = imp; - m_value = val; } - property_value(const property_value& val) + property_value(const string& str, bool important, property_type type = prop_type_string) + : m_string(str), m_type(type), m_important(important) { - m_value = val.m_value; - m_important = val.m_important; } - + property_value(const string_vector& vec, bool important) + : m_string_vector(vec), m_type(prop_type_string_vector), m_important(important) + { + } + property_value(const css_length& length, bool important) + : m_length(length), m_type(prop_type_length), m_important(important) + { + } + property_value(const length_vector& vec, bool important) + : m_length_vector(vec), m_type(prop_type_length_vector), m_important(important) + { + } + property_value(float number, bool important) + : m_number(number), m_type(prop_type_number), m_important(important) + { + } + property_value(int enum_item, bool important) + : m_enum_item(enum_item), m_type(prop_type_enum_item), m_important(important) + { + } + property_value(const int_vector& vec, bool important) + : m_enum_item_vector(vec), m_type(prop_type_enum_item_vector), m_important(important) + { + } + property_value(web_color color, bool important) + : m_color(color), m_type(prop_type_color), m_important(important) + { + } + property_value(const size_vector& vec, bool important) + : m_size_vector(vec), m_type(prop_type_size_vector), m_important(important) + { + } + ~property_value() + { + switch (m_type) + { + case prop_type_string: + case prop_type_var: + m_string.~string(); + break; + case prop_type_string_vector: + m_string_vector.~string_vector(); + break; + case prop_type_length: + m_length.~css_length(); + break; + case prop_type_length_vector: + m_length_vector.~length_vector(); + break; + case prop_type_enum_item_vector: + m_enum_item_vector.~int_vector(); + break; + case prop_type_color: + m_color.~web_color(); + break; + case prop_type_size_vector: + m_size_vector.~size_vector(); + break; + } + } property_value& operator=(const property_value& val) { - m_value = val.m_value; - m_important = val.m_important; + this->~property_value(); + + switch (val.m_type) + { + case prop_type_invalid: + new(this) property_value(); + break; + case prop_type_inherit: + new(this) property_value(val.m_important, val.m_type); + break; + case prop_type_string: + case prop_type_var: + new(this) property_value(val.m_string, val.m_important, val.m_type); + break; + case prop_type_string_vector: + new(this) property_value(val.m_string_vector, val.m_important); + break; + case prop_type_enum_item: + new(this) property_value(val.m_enum_item, val.m_important); + break; + case prop_type_enum_item_vector: + new(this) property_value(val.m_enum_item_vector, val.m_important); + break; + case prop_type_length: + new(this) property_value(val.m_length, val.m_important); + break; + case prop_type_length_vector: + new(this) property_value(val.m_length_vector, val.m_important); + break; + case prop_type_number: + new(this) property_value(val.m_number, val.m_important); + break; + case prop_type_color: + new(this) property_value(val.m_color, val.m_important); + break; + case prop_type_size_vector: + new(this) property_value(val.m_size_vector, val.m_important); + break; + } + return *this; } }; - typedef std::map<tstring, property_value> props_map; + typedef std::map<string_id, property_value> props_map; class style { @@ -43,52 +165,47 @@ namespace litehtml typedef std::shared_ptr<style> ptr; typedef std::vector<style::ptr> vector; private: - props_map m_properties; - static string_map m_valid_values; + props_map m_properties; + static std::map<string_id, string> m_valid_values; public: - style(); - style(const style& val); - virtual ~style(); - - void operator=(const style& val) + void add(const string& txt, const string& baseurl = "", document_container* container = nullptr) { - m_properties = val.m_properties; + parse(txt, baseurl, container); } - void add(const tchar_t* txt, const tchar_t* baseurl) - { - parse(txt, baseurl); - } + void add_property(string_id name, const string& val, const string& baseurl = "", bool important = false, document_container* container = nullptr); - void add_property(const tchar_t* name, const tchar_t* val, const tchar_t* baseurl, bool important); - - const tchar_t* get_property(const tchar_t* name) const - { - if(name) - { - props_map::const_iterator f = m_properties.find(name); - if(f != m_properties.end()) - { - return f->second.m_value.c_str(); - } - } - return 0; - } + const property_value& get_property(string_id name) const; - void combine(const litehtml::style& src); + void combine(const style& src); void clear() { m_properties.clear(); } + void subst_vars(const element* el); + private: - void parse_property(const tstring& txt, const tchar_t* baseurl); - void parse(const tchar_t* txt, const tchar_t* baseurl); - void parse_short_border(const tstring& prefix, const tstring& val, bool important); - void parse_short_background(const tstring& val, const tchar_t* baseurl, bool important); - void parse_short_font(const tstring& val, bool important); - void add_parsed_property(const tstring& name, const tstring& val, bool important); - void remove_property(const tstring& name, bool important); + void parse_property(const string& txt, const string& baseurl, document_container* container); + void parse(const string& txt, const string& baseurl, document_container* container); + void parse_background(const string& val, const string& baseurl, bool important, document_container* container); + bool parse_one_background(const string& val, document_container* container, background& bg); + void parse_background_image(const string& val, const string& baseurl, bool important); + // parse comma-separated list of keywords + void parse_keyword_comma_list(string_id name, const string& val, bool important); + void parse_background_position(const string& val, bool important); + bool parse_one_background_position(const string& val, css_length& x, css_length& y); + void parse_background_size(const string& val, bool important); + 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); + 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]); + static void subst_vars_(string& str, const element* el); + + void add_parsed_property(string_id name, const property_value& propval); + void remove_property(string_id name, bool important); }; } diff --git a/src/plugins/litehtml_viewer/litehtml/stylesheet.cpp b/src/plugins/litehtml_viewer/litehtml/stylesheet.cpp @@ -4,31 +4,31 @@ #include "document.h" -void litehtml::css::parse_stylesheet(const tchar_t* str, const tchar_t* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media) +void litehtml::css::parse_stylesheet(const char* str, const char* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media) { - tstring text = str; + string text = str; // remove comments - tstring::size_type c_start = text.find(_t("/*")); - while(c_start != tstring::npos) + string::size_type c_start = text.find("/*"); + while(c_start != string::npos) { - tstring::size_type c_end = text.find(_t("*/"), c_start + 2); + string::size_type c_end = text.find("*/", c_start + 2); text.erase(c_start, c_end - c_start + 2); - c_start = text.find(_t("/*")); + c_start = text.find("/*"); } - tstring::size_type pos = text.find_first_not_of(_t(" \n\r\t")); - while(pos != tstring::npos) + string::size_type pos = text.find_first_not_of(" \n\r\t"); + while(pos != string::npos) { - while(pos != tstring::npos && text[pos] == _t('@')) + while(pos != string::npos && text[pos] == '@') { - tstring::size_type sPos = pos; - pos = text.find_first_of(_t("{;"), pos); - if(pos != tstring::npos && text[pos] == _t('{')) + string::size_type sPos = pos; + pos = text.find_first_of("{;", pos); + if(pos != string::npos && text[pos] == '{') { - pos = find_close_bracket(text, pos, _t('{'), _t('}')); + pos = find_close_bracket(text, pos, '{', '}'); } - if(pos != tstring::npos) + if(pos != string::npos) { parse_atrule(text.substr(sPos, pos - sPos + 1), baseurl, doc, media); } else @@ -36,25 +36,26 @@ void litehtml::css::parse_stylesheet(const tchar_t* str, const tchar_t* baseurl, parse_atrule(text.substr(sPos), baseurl, doc, media); } - if(pos != tstring::npos) + if(pos != string::npos) { - pos = text.find_first_not_of(_t(" \n\r\t"), pos + 1); + pos = text.find_first_not_of(" \n\r\t", pos + 1); } } - if(pos == tstring::npos) + if(pos == string::npos) { break; } - tstring::size_type style_start = text.find(_t("{"), pos); - tstring::size_type style_end = text.find(_t("}"), pos); - if(style_start != tstring::npos && style_end != tstring::npos) + string::size_type style_start = text.find('{', pos); + string::size_type style_end = text.find('}', pos); + if(style_start != string::npos && style_end != string::npos) { - style::ptr st = std::make_shared<style>(); - st->add(text.substr(style_start + 1, style_end - style_start - 1).c_str(), baseurl); + auto str_style = text.substr(style_start + 1, style_end - style_start - 1); + style::ptr style = std::make_shared<litehtml::style>(); + style->add(str_style, baseurl ? baseurl : "", doc->container()); - parse_selectors(text.substr(pos, style_start - pos), st, media); + parse_selectors(text.substr(pos, style_start - pos), style, media); if(media && doc) { @@ -64,34 +65,34 @@ void litehtml::css::parse_stylesheet(const tchar_t* str, const tchar_t* baseurl, pos = style_end + 1; } else { - pos = tstring::npos; + pos = string::npos; } - if(pos != tstring::npos) + if(pos != string::npos) { - pos = text.find_first_not_of(_t(" \n\r\t"), pos); + pos = text.find_first_not_of(" \n\r\t", pos); } } } -void litehtml::css::parse_css_url( const tstring& str, tstring& url ) +void litehtml::css::parse_css_url( const string& str, string& url ) { - url = _t(""); - size_t pos1 = str.find(_t('(')); - size_t pos2 = str.find(_t(')')); - if(pos1 != tstring::npos && pos2 != tstring::npos) + url = ""; + size_t pos1 = str.find('('); + size_t pos2 = str.find(')'); + if(pos1 != string::npos && pos2 != string::npos) { url = str.substr(pos1 + 1, pos2 - pos1 - 1); if(url.length()) { - if(url[0] == _t('\'') || url[0] == _t('"')) + if(url[0] == '\'' || url[0] == '"') { url.erase(0, 1); } } if(url.length()) { - if(url[url.length() - 1] == _t('\'') || url[url.length() - 1] == _t('"')) + if(url[url.length() - 1] == '\'' || url[url.length() - 1] == '"') { url.erase(url.length() - 1, 1); } @@ -99,24 +100,24 @@ void litehtml::css::parse_css_url( const tstring& str, tstring& url ) } } -bool litehtml::css::parse_selectors( const tstring& txt, const litehtml::style::ptr& styles, const media_query_list::ptr& media ) +bool litehtml::css::parse_selectors( const string& txt, const style::ptr& styles, const media_query_list::ptr& media ) { - tstring selector = txt; + string selector = txt; trim(selector); string_vector tokens; - split_string(selector, tokens, _t(",")); + split_string(selector, tokens, ","); bool added_something = false; - for(string_vector::iterator tok = tokens.begin(); tok != tokens.end(); tok++) + for(auto & token : tokens) { - css_selector::ptr selector = std::make_shared<css_selector>(media); - selector->m_style = styles; - trim(*tok); - if(selector->parse(*tok)) + css_selector::ptr new_selector = std::make_shared<css_selector>(media); + new_selector->m_style = styles; + trim(token); + if(new_selector->parse(token)) { - selector->calc_specificity(); - add_selector(selector); + new_selector->calc_specificity(); + add_selector(new_selector); added_something = true; } } @@ -134,23 +135,23 @@ void litehtml::css::sort_selectors() ); } -void litehtml::css::parse_atrule(const tstring& text, const tchar_t* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media) +void litehtml::css::parse_atrule(const string& text, const char* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media) { - if(text.substr(0, 7) == _t("@import")) + if(text.substr(0, 7) == "@import") { int sPos = 7; - tstring iStr; + string iStr; iStr = text.substr(sPos); - if(iStr[iStr.length() - 1] == _t(';')) + if(iStr[iStr.length() - 1] == ';') { iStr.erase(iStr.length() - 1); } trim(iStr); string_vector tokens; - split_string(iStr, tokens, _t(" "), _t(""), _t("(\"")); + split_string(iStr, tokens, " ", "", "(\""); if(!tokens.empty()) { - tstring url; + string url; parse_css_url(tokens.front(), url); if(url.empty()) { @@ -162,8 +163,8 @@ void litehtml::css::parse_atrule(const tstring& text, const tchar_t* baseurl, co document_container* doc_cont = doc->container(); if(doc_cont) { - tstring css_text; - tstring css_baseurl; + string css_text; + string css_baseurl; if(baseurl) { css_baseurl = baseurl; @@ -174,12 +175,12 @@ void litehtml::css::parse_atrule(const tstring& text, const tchar_t* baseurl, co media_query_list::ptr new_media = media; if(!tokens.empty()) { - tstring media_str; - for(string_vector::iterator iter = tokens.begin(); iter != tokens.end(); iter++) + string media_str; + for(auto iter = tokens.begin(); iter != tokens.end(); iter++) { if(iter != tokens.begin()) { - media_str += _t(" "); + media_str += " "; } media_str += (*iter); } @@ -194,18 +195,18 @@ void litehtml::css::parse_atrule(const tstring& text, const tchar_t* baseurl, co } } } - } else if(text.substr(0, 6) == _t("@media")) + } else if(text.substr(0, 6) == "@media") { - tstring::size_type b1 = text.find_first_of(_t('{')); - tstring::size_type b2 = text.find_last_of(_t('}')); - if(b1 != tstring::npos) + string::size_type b1 = text.find_first_of('{'); + string::size_type b2 = text.find_last_of('}'); + if(b1 != string::npos) { - tstring media_type = text.substr(6, b1 - 6); + string media_type = text.substr(6, b1 - 6); trim(media_type); media_query_list::ptr new_media = media_query_list::create_from_string(media_type, doc); - tstring media_style; - if(b2 != tstring::npos) + string media_style; + if(b2 != string::npos) { media_style = text.substr(b1 + 1, b2 - b1 - 1); } else diff --git a/src/plugins/litehtml_viewer/litehtml/stylesheet.h b/src/plugins/litehtml_viewer/litehtml/stylesheet.h @@ -12,15 +12,8 @@ namespace litehtml { css_selector::vector m_selectors; public: - css() - { - - } - - ~css() - { - - } + css() = default; + ~css() = default; const css_selector::vector& selectors() const { @@ -32,18 +25,18 @@ namespace litehtml m_selectors.clear(); } - void parse_stylesheet(const tchar_t* str, const tchar_t* baseurl, const std::shared_ptr <document>& doc, const media_query_list::ptr& media); + void parse_stylesheet(const char* str, const char* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media); void sort_selectors(); - static void parse_css_url(const tstring& str, tstring& url); + static void parse_css_url(const string& str, string& url); private: - void parse_atrule(const tstring& text, const tchar_t* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media); - void add_selector(css_selector::ptr selector); - bool parse_selectors(const tstring& txt, const litehtml::style::ptr& styles, const media_query_list::ptr& media); + void parse_atrule(const string& text, const char* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media); + void add_selector(const css_selector::ptr& selector); + bool parse_selectors(const string& txt, const style::ptr& styles, const media_query_list::ptr& media); }; - inline void litehtml::css::add_selector( css_selector::ptr selector ) + inline void litehtml::css::add_selector( const css_selector::ptr& selector ) { selector->m_order = (int) m_selectors.size(); m_selectors.push_back(selector); diff --git a/src/plugins/litehtml_viewer/litehtml/table.cpp b/src/plugins/litehtml_viewer/litehtml/table.cpp @@ -1,13 +1,14 @@ #include "html.h" #include "table.h" -#include "html_tag.h" +#include "element.h" +#include "render_item.h" -void litehtml::table_grid::add_cell(element::ptr& el) +void litehtml::table_grid::add_cell(const std::shared_ptr<render_item>& el) { table_cell cell; cell.el = el; - cell.colspan = t_atoi(el->get_attr(_t("colspan"), _t("1"))); - cell.rowspan = t_atoi(el->get_attr(_t("rowspan"), _t("1"))); + cell.colspan = atoi(el->src_el()->get_attr("colspan", "1")); + cell.rowspan = atoi(el->src_el()->get_attr("rowspan", "1")); cell.borders = el->get_borders(); while( is_rowspanned( (int) m_cells.size() - 1, (int) m_cells.back().size() ) ) @@ -24,13 +25,12 @@ void litehtml::table_grid::add_cell(element::ptr& el) } -void litehtml::table_grid::begin_row(element::ptr& row) +void litehtml::table_grid::begin_row(const std::shared_ptr<render_item>& row) { std::vector<table_cell> r; m_cells.push_back(r); m_rows.push_back(table_row(0, row)); - } @@ -56,16 +56,16 @@ void litehtml::table_grid::finish() { m_rows_count = (int) m_cells.size(); m_cols_count = 0; - for(int i = 0; i < (int) m_cells.size(); i++) + for(auto& cell : m_cells) { - m_cols_count = std::max(m_cols_count, (int) m_cells[i].size()); + m_cols_count = std::max(m_cols_count, (int) cell.size()); } - for(int i = 0; i < (int) m_cells.size(); i++) + for(auto& cell : m_cells) { - for(int j = (int) m_cells[i].size(); j < m_cols_count; j++) + for(int j = (int) cell.size(); j < m_cols_count; j++) { table_cell empty_cell; - m_cells[i].push_back(empty_cell); + cell.push_back(empty_cell); } } @@ -117,9 +117,9 @@ void litehtml::table_grid::finish() if(cell(col, row)->el && cell(col, row)->colspan <= 1) { - if (!cell(col, row)->el->get_css_width().is_predefined() && m_columns[col].css_width.is_predefined()) + if (!cell(col, row)->el->src_el()->css().get_width().is_predefined() && m_columns[col].css_width.is_predefined()) { - m_columns[col].css_width = cell(col, row)->el->get_css_width(); + m_columns[col].css_width = cell(col, row)->el->src_el()->css().get_width(); } } } @@ -129,9 +129,9 @@ void litehtml::table_grid::finish() { for(int row = 0; row < m_rows_count; row++) { - if(cell(col, row)->el) + if(cell(col, row)->el && cell(col, row)->colspan == 1) { - cell(col, row)->el->set_css_width(m_columns[col].css_width); + cell(col, row)->el->src_el()->css_w().set_width(m_columns[col].css_width); } } } @@ -143,7 +143,7 @@ litehtml::table_cell* litehtml::table_grid::cell( int t_col, int t_row ) { return &m_cells[t_row][t_col]; } - return 0; + return nullptr; } void litehtml::table_grid::distribute_max_width( int width, int start, int end ) @@ -243,25 +243,25 @@ void litehtml::table_grid::distribute_width( int width, int start, int end ) if(!distribute_columns.empty() || step == 2) { int cols_width = 0; - for(std::vector<table_column*>::iterator col = distribute_columns.begin(); col != distribute_columns.end(); col++) + for(const auto& column : distribute_columns) { - cols_width += (*col)->max_width - (*col)->min_width; + cols_width += column->max_width - column->min_width; } if(cols_width) { int add = width / (int) distribute_columns.size(); - for(std::vector<table_column*>::iterator col = distribute_columns.begin(); col != distribute_columns.end(); col++) + for(const auto& column : distribute_columns) { - add = round_f( (float) width * ((float) ((*col)->max_width - (*col)->min_width) / (float) cols_width) ); - if((*col)->width + add >= (*col)->min_width) + add = round_f( (float) width * ((float) (column->max_width - column->min_width) / (float) cols_width) ); + if(column->width + add >= column->min_width) { - (*col)->width += add; + column->width += add; added_width += add; } else { - added_width += ((*col)->width - (*col)->min_width) * (add / abs(add)); - (*col)->width = (*col)->min_width; + added_width += (column->width - column->min_width) * (add / abs(add)); + column->width = column->min_width; } } if(added_width < width && step) @@ -359,7 +359,7 @@ int litehtml::table_grid::calc_table_width(int block_width, bool is_auto, int& m fixed_width += m_columns[col].width; } } - float scale = (float) (100.0 / percent); + auto scale = (float) (100.0 / percent); cur_width = 0; for(int col = 0; col < m_cols_count; col++) { @@ -375,6 +375,34 @@ int litehtml::table_grid::calc_table_width(int block_width, bool is_auto, int& m } cur_width += m_columns[col].width; } + // If the table is still too wide shrink columns with % widths + if(cur_width > block_width) + { + while(true) + { + bool shrunk = false; + for(int col = 0; col < m_cols_count; col++) + { + if(!m_columns[col].css_width.is_predefined() && m_columns[col].css_width.units() == css_units_percentage) + { + if(m_columns[col].width > m_columns[col].min_width) + { + m_columns[col].width--; + cur_width--; + shrunk = true; + if(cur_width == block_width) + { + break; + } + } + } + } + if(cur_width == block_width || !shrunk) + { + break; + } + } + } } return cur_width; } @@ -388,7 +416,7 @@ void litehtml::table_grid::clear() m_rows.clear(); } -void litehtml::table_grid::calc_horizontal_positions( margins& table_borders, border_collapse bc, int bdr_space_x) +void litehtml::table_grid::calc_horizontal_positions( const margins& table_borders, border_collapse bc, int bdr_space_x) { if(bc == border_collapse_separate) { @@ -420,7 +448,7 @@ void litehtml::table_grid::calc_horizontal_positions( margins& table_borders, bo } } -void litehtml::table_grid::calc_vertical_positions( margins& table_borders, border_collapse bc, int bdr_space_y ) +void litehtml::table_grid::calc_vertical_positions( const margins& table_borders, border_collapse bc, int bdr_space_y ) { if(bc == border_collapse_separate) { @@ -564,3 +592,18 @@ int& litehtml::table_column_accessor_width::get( table_column& col ) { return col.width; } + +litehtml::table_row::table_row(int h, const std::shared_ptr<render_item>& row) +{ + min_height = 0; + height = h; + el_row = row; + border_bottom = 0; + border_top = 0; + top = 0; + bottom = 0; + if (row) + { + css_height = row->src_el()->css().get_height(); + } +} diff --git a/src/plugins/litehtml_viewer/litehtml/table.h b/src/plugins/litehtml_viewer/litehtml/table.h @@ -1,8 +1,14 @@ #ifndef LH_TABLE_H #define LH_TABLE_H +#include <vector> +#include <memory> +#include "css_length.h" + namespace litehtml { + class render_item; + struct table_row { typedef std::vector<table_row> vector; @@ -10,7 +16,7 @@ namespace litehtml int height; int border_top; int border_bottom; - element::ptr el_row; + std::shared_ptr<render_item> el_row; int top; int bottom; css_length css_height; @@ -28,20 +34,7 @@ namespace litehtml css_height.predef(0); } - table_row(int h, element::ptr& row) - { - min_height = 0; - height = h; - el_row = row; - border_bottom = 0; - border_top = 0; - top = 0; - bottom = 0; - if (row) - { - css_height = row->get_css_height(); - } - } + table_row(int h, const std::shared_ptr<render_item>& row); table_row(const table_row& val) { @@ -55,7 +48,7 @@ namespace litehtml el_row = val.el_row; } - table_row(table_row&& val) + table_row(table_row&& val) noexcept { min_height = val.min_height; top = val.top; @@ -122,29 +115,32 @@ namespace litehtml { public: virtual int& get(table_column& col) = 0; + + protected: + ~table_column_accessor() = default; }; - class table_column_accessor_max_width : public table_column_accessor + class table_column_accessor_max_width final : public table_column_accessor { public: - virtual int& get(table_column& col); + int& get(table_column& col) override; }; - class table_column_accessor_min_width : public table_column_accessor + class table_column_accessor_min_width final : public table_column_accessor { public: - virtual int& get(table_column& col); + int& get(table_column& col) override; }; - class table_column_accessor_width : public table_column_accessor + class table_column_accessor_width final : public table_column_accessor { public: - virtual int& get(table_column& col); + int& get(table_column& col) override; }; struct table_cell { - element::ptr el; + std::shared_ptr<render_item> el; int colspan; int rowspan; int min_width; @@ -182,8 +178,8 @@ namespace litehtml borders = val.borders; } - table_cell(const table_cell&& val) - { + table_cell(table_cell&& val) noexcept + { el = std::move(val.el); colspan = val.colspan; rowspan = val.rowspan; @@ -207,33 +203,40 @@ namespace litehtml rows m_cells; table_column::vector m_columns; table_row::vector m_rows; + std::vector<std::shared_ptr<render_item>> m_captions; + int m_captions_height; public: table_grid() { m_rows_count = 0; m_cols_count = 0; + m_captions_height = 0; } void clear(); - void begin_row(element::ptr& row); - void add_cell(element::ptr& el); + void begin_row(const std::shared_ptr<render_item>& row); + void add_cell(const std::shared_ptr<render_item>& el); bool is_rowspanned(int r, int c); void finish(); table_cell* cell(int t_col, int t_row); table_column& column(int c) { return m_columns[c]; } table_row& row(int r) { return m_rows[r]; } + std::vector<std::shared_ptr<render_item>>& captions() { return m_captions; } + + int rows_count() const { return m_rows_count; } + int cols_count() const { return m_cols_count; } - int rows_count() { return m_rows_count; } - int cols_count() { return m_cols_count; } + void captions_height(int height) { m_captions_height = height; } + int captions_height() const { return m_captions_height; } void distribute_max_width(int width, int start, int end); void distribute_min_width(int width, int start, int end); void distribute_width(int width, int start, int end); void distribute_width(int width, int start, int end, table_column_accessor* acc); int calc_table_width(int block_width, bool is_auto, int& min_table_width, int& max_table_width); - void calc_horizontal_positions(margins& table_borders, border_collapse bc, int bdr_space_x); - void calc_vertical_positions(margins& table_borders, border_collapse bc, int bdr_space_y); + void calc_horizontal_positions(const margins& table_borders, border_collapse bc, int bdr_space_x); + void calc_vertical_positions(const margins& table_borders, border_collapse bc, int bdr_space_y); void calc_rows_height(int blockHeight, int borderSpacingY); }; } diff --git a/src/plugins/litehtml_viewer/litehtml/tstring_view.cpp b/src/plugins/litehtml_viewer/litehtml/tstring_view.cpp @@ -0,0 +1,46 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "tstring_view.h" + +namespace litehtml { + + +std::basic_ostream<tstring_view::value_type>& operator<<( + std::basic_ostream<tstring_view::value_type>& os, + tstring_view str) +{ + if (os.good()) { + os.write(str.data(), str.size()); + } + + return os; +} + +} // namespace litehtml +\ No newline at end of file diff --git a/src/plugins/litehtml_viewer/litehtml/tstring_view.h b/src/plugins/litehtml_viewer/litehtml/tstring_view.h @@ -0,0 +1,136 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef LITEHTML_TSTRING_VIEW_H__ +#define LITEHTML_TSTRING_VIEW_H__ + +#include <cstddef> +#include <ostream> + +#include "os_types.h" + +namespace litehtml { + +// tstring_view is a string reference type that provides a view into a string +// that is owned elsewhere (e.g., by a std::string object). + +// tstring_view implements the same interface as std::base_string_view in the +// standard library. When litehtml moves to C++17 consider replacing the +// tstring_view implementation with the standard library implementations +// (e.g., via a using statement). + +class tstring_view { +public: + using value_type = char; + + using pointer = char*; + + using const_pointer = const char*; + + using reference = char&; + + using const_reference = const char&; + + using iterator = const_pointer; + + using const_iterator = const_pointer; + + using size_type = size_t; + + using difference_type = std::ptrdiff_t; + +public: + tstring_view() = default; + + tstring_view(const tstring_view& other) = default; + + tstring_view(const_pointer s, size_type size) + : data_(s) + , size_(size) + { + } + + constexpr const_iterator begin() const + { + return data_; + } + + constexpr const_iterator cbegin() const + { + return data_; + } + + constexpr const_iterator end() const + { + return data_ + size_; + } + + constexpr const_iterator cend() const + { + return data_ + size_; + } + + constexpr const_reference operator[](size_type offset) const + { + return *(data_ + offset); + } + + constexpr const_pointer data() const + { + return data_; + } + + size_type size() const + { + return size_; + } + + size_type length() const + { + return size_; + } + + bool empty() const + { + return (size_ == 0); + } + +private: + const_pointer data_ = nullptr; + + size_type size_ = 0; +}; + +std::basic_ostream<tstring_view::value_type>& operator<<( + std::basic_ostream<tstring_view::value_type>&, + tstring_view str); + +} // namespace litehtml + +#endif // LITEHTML_TSTRING_VIEW_H__ diff --git a/src/plugins/litehtml_viewer/litehtml/types.h b/src/plugins/litehtml_viewer/litehtml/types.h @@ -1,7 +1,7 @@ #ifndef LH_TYPES_H #define LH_TYPES_H -#include <stdlib.h> +#include <cstdlib> #include <memory> #include <map> #include <vector> @@ -11,10 +11,10 @@ namespace litehtml class document; class element; - typedef std::map<litehtml::tstring, litehtml::tstring> string_map; - typedef std::vector< std::shared_ptr<litehtml::element> > elements_vector; - typedef std::vector<int> int_vector; - typedef std::vector<litehtml::tstring> string_vector; + typedef std::map<string, string> string_map; + typedef std::vector< std::shared_ptr<element> > elements_vector; + typedef std::vector<int> int_vector; + typedef std::vector<string> string_vector; const unsigned int font_decoration_none = 0x00; const unsigned int font_decoration_underline = 0x01; @@ -45,10 +45,12 @@ namespace litehtml int width; int height; - size() + size(int w, int h) : width(w), height(h) + { + } + + size() : width(0), height(0) { - width = 0; - height = 0; } }; @@ -162,7 +164,7 @@ namespace litehtml x_height = 0; draw_spaces = true; } - int base_line() { return descent; } + int base_line() const { return descent; } }; struct font_item @@ -171,7 +173,7 @@ namespace litehtml font_metrics metrics; }; - typedef std::map<tstring, font_item> fonts_map; + typedef std::map<string, font_item> fonts_map; enum draw_flag { @@ -182,7 +184,81 @@ namespace litehtml draw_positioned, }; -#define style_display_strings _t("none;block;inline;inline-block;inline-table;list-item;table;table-caption;table-cell;table-column;table-column-group;table-footer-group;table-header-group;table-row;table-row-group;inline-text") + struct containing_block_context + { + enum cbc_value_type + { + cbc_value_type_absolute, // width/height of containing block is defined as absolute value + cbc_value_type_percentage, // width/height of containing block is defined as percentage + cbc_value_type_auto, // width/height of containing block is defined as auto + cbc_value_type_none, // min/max width/height of containing block is defined as none + }; + + struct typed_int + { + int value; + cbc_value_type type; + + typed_int(int val, cbc_value_type tp) + { + value = val; + type = tp; + } + + operator int() const + { + return value; + } + + typed_int& operator=(int val) + { + value = val; + return *this; + } + + typed_int& operator=(const typed_int& v) + { + value = v.value; + type = v.type; + return *this; + } + }; + + typed_int width; // width of the containing block + typed_int render_width; + typed_int min_width; + typed_int max_width; + + typed_int height; // height of the containing block + typed_int min_height; + typed_int max_height; + + int context_idx; + + containing_block_context() : + width(0, cbc_value_type_auto), + render_width(0, cbc_value_type_auto), + min_width(0, cbc_value_type_none), + max_width(0, cbc_value_type_none), + height(0, cbc_value_type_auto), + min_height(0, cbc_value_type_none), + max_height(0, cbc_value_type_none), + context_idx(0) + {} + + containing_block_context new_width(int w) 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; + } + return ret; + } + }; + +#define style_display_strings "none;block;inline;inline-block;inline-table;list-item;table;table-caption;table-cell;table-column;table-column-group;table-footer-group;table-header-group;table-row;table-row-group;inline-text;flex;inline-flex" enum style_display { @@ -202,68 +278,68 @@ namespace litehtml display_table_row, display_table_row_group, display_inline_text, + display_flex, + display_inline_flex, }; - enum style_border +#define font_size_strings "xx-small;x-small;small;medium;large;x-large;xx-large;smaller;larger" + + enum font_size { - borderNope, - borderNone, - borderHidden, - borderDotted, - borderDashed, - borderSolid, - borderDouble + font_size_xx_small, + font_size_x_small, + font_size_small, + font_size_medium, + font_size_large, + font_size_x_large, + font_size_xx_large, + font_size_smaller, + font_size_larger, }; -#define font_size_strings _t("xx-small;x-small;small;medium;large;x-large;xx-large;smaller;larger") +#define line_height_strings "normal" - enum font_size + enum line_height { - fontSize_xx_small, - fontSize_x_small, - fontSize_small, - fontSize_medium, - fontSize_large, - fontSize_x_large, - fontSize_xx_large, - fontSize_smaller, - fontSize_larger, + line_height_normal }; -#define font_style_strings _t("normal;italic") +#define font_style_strings "normal;italic" enum font_style { - fontStyleNormal, - fontStyleItalic + font_style_normal, + font_style_italic }; -#define font_variant_strings _t("normal;small-caps") +#define font_variant_strings "normal;small-caps" enum font_variant { font_variant_normal, - font_variant_italic + font_variant_small_caps }; -#define font_weight_strings _t("normal;bold;bolder;lighter;100;200;300;400;500;600;700") +#define font_weight_strings "normal;bold;bolder;lighter;100;200;300;400;500;600;700;800;900" enum font_weight { - fontWeightNormal, - fontWeightBold, - fontWeightBolder, - fontWeightLighter, - fontWeight100, - fontWeight200, - fontWeight300, - fontWeight400, - fontWeight500, - fontWeight600, - fontWeight700 + font_weight_normal, + font_weight_bold, + font_weight_bolder, + font_weight_lighter, + font_weight_100, + font_weight_200, + font_weight_300, + font_weight_400, + font_weight_500, + font_weight_600, + font_weight_700, + font_weight_800, + font_weight_900 }; -#define list_style_type_strings _t("none;circle;disc;square;armenian;cjk-ideographic;decimal;decimal-leading-zero;georgian;hebrew;hiragana;hiragana-iroha;katakana;katakana-iroha;lower-alpha;lower-greek;lower-latin;lower-roman;upper-alpha;upper-latin;upper-roman") +#define list_style_type_strings "none;circle;disc;square;armenian;cjk-ideographic;decimal;decimal-leading-zero;georgian;hebrew;hiragana;hiragana-iroha;katakana;katakana-iroha;lower-alpha;lower-greek;lower-latin;lower-roman;upper-alpha;upper-latin;upper-roman" enum list_style_type { @@ -290,7 +366,7 @@ namespace litehtml list_style_type_upper_roman, }; -#define list_style_position_strings _t("inside;outside") +#define list_style_position_strings "inside;outside" enum list_style_position { @@ -298,7 +374,7 @@ namespace litehtml list_style_position_outside }; -#define vertical_align_strings _t("baseline;sub;super;top;text-top;middle;bottom;text-bottom") +#define vertical_align_strings "baseline;sub;super;top;text-top;middle;bottom;text-bottom" enum vertical_align { @@ -312,7 +388,7 @@ namespace litehtml va_text_bottom }; -#define border_width_strings _t("thin;medium;thick") +#define border_width_strings "thin;medium;thick" enum border_width { @@ -321,7 +397,12 @@ namespace litehtml border_width_thick }; -#define border_style_strings _t("none;hidden;dotted;dashed;solid;double;groove;ridge;inset;outset") + const float border_width_thin_value = 1; + const float border_width_medium_value = 3; + const float border_width_thick_value = 5; + const float border_width_values[] = { border_width_thin_value, border_width_medium_value, border_width_thick_value }; + +#define border_style_strings "none;hidden;dotted;dashed;solid;double;groove;ridge;inset;outset" enum border_style { @@ -337,7 +418,7 @@ namespace litehtml border_style_outset }; -#define element_float_strings _t("none;left;right") +#define element_float_strings "none;left;right" enum element_float { @@ -346,7 +427,7 @@ namespace litehtml float_right }; -#define element_clear_strings _t("none;left;right;both") +#define element_clear_strings "none;left;right;both" enum element_clear { @@ -356,9 +437,9 @@ namespace litehtml clear_both }; -#define css_units_strings _t("none;%;in;cm;mm;em;ex;pt;pc;px;dpi;dpcm;vw;vh;vmin;vmax;rem") +#define css_units_strings "none;%;in;cm;mm;em;ex;pt;pc;px;dpi;dpcm;vw;vh;vmin;vmax;rem" - enum css_units + enum css_units : byte { css_units_none, css_units_percentage, @@ -379,7 +460,7 @@ namespace litehtml css_units_rem, }; -#define background_attachment_strings _t("scroll;fixed") +#define background_attachment_strings "scroll;fixed" enum background_attachment { @@ -387,7 +468,7 @@ namespace litehtml background_attachment_fixed }; -#define background_repeat_strings _t("repeat;repeat-x;repeat-y;no-repeat") +#define background_repeat_strings "repeat;repeat-x;repeat-y;no-repeat" enum background_repeat { @@ -397,7 +478,7 @@ namespace litehtml background_repeat_no_repeat }; -#define background_box_strings _t("border-box;padding-box;content-box") +#define background_box_strings "border-box;padding-box;content-box" enum background_box { @@ -406,7 +487,18 @@ namespace litehtml background_box_content }; -#define element_position_strings _t("static;relative;absolute;fixed") +#define background_position_strings "top;bottom;left;right;center" + + enum background_position + { + background_position_top, + background_position_bottom, + background_position_left, + background_position_right, + background_position_center, + }; + +#define element_position_strings "static;relative;absolute;fixed" enum element_position { @@ -416,7 +508,7 @@ namespace litehtml element_position_fixed, }; -#define text_align_strings _t("left;right;center;justify") +#define text_align_strings "left;right;center;justify" enum text_align { @@ -426,7 +518,7 @@ namespace litehtml text_align_justify }; -#define text_transform_strings _t("none;capitalize;uppercase;lowercase") +#define text_transform_strings "none;capitalize;uppercase;lowercase" enum text_transform { @@ -436,7 +528,7 @@ namespace litehtml text_transform_lowercase }; -#define white_space_strings _t("normal;nowrap;pre;pre-line;pre-wrap") +#define white_space_strings "normal;nowrap;pre;pre-line;pre-wrap" enum white_space { @@ -447,7 +539,7 @@ namespace litehtml white_space_pre_wrap }; -#define overflow_strings _t("visible;hidden;scroll;auto;no-display;no-content") +#define overflow_strings "visible;hidden;scroll;auto;no-display;no-content" enum overflow { @@ -459,7 +551,7 @@ namespace litehtml overflow_no_content }; -#define background_size_strings _t("auto;cover;contain") +#define background_size_strings "auto;cover;contain" enum background_size { @@ -468,7 +560,7 @@ namespace litehtml background_size_contain, }; -#define visibility_strings _t("visible;hidden;collapse") +#define visibility_strings "visible;hidden;collapse" enum visibility { @@ -477,7 +569,7 @@ namespace litehtml visibility_collapse, }; -#define border_collapse_strings _t("collapse;separate") +#define border_collapse_strings "collapse;separate" enum border_collapse { @@ -485,26 +577,7 @@ namespace litehtml border_collapse_separate, }; - -#define pseudo_class_strings _t("only-child;only-of-type;first-child;first-of-type;last-child;last-of-type;nth-child;nth-of-type;nth-last-child;nth-last-of-type;not;lang") - - enum pseudo_class - { - pseudo_class_only_child, - pseudo_class_only_of_type, - pseudo_class_first_child, - pseudo_class_first_of_type, - pseudo_class_last_child, - pseudo_class_last_of_type, - pseudo_class_nth_child, - pseudo_class_nth_of_type, - pseudo_class_nth_last_child, - pseudo_class_nth_last_of_type, - pseudo_class_not, - pseudo_class_lang, - }; - -#define content_property_string _t("none;normal;open-quote;close-quote;no-open-quote;no-close-quote") +#define content_property_string "none;normal;open-quote;close-quote;no-open-quote;no-close-quote" enum content_property { @@ -516,15 +589,15 @@ namespace litehtml content_property_no_close_quote, }; + class render_item; struct floated_box { - typedef std::vector<floated_box> vector; - - position pos; - element_float float_side; - element_clear clear_floats; - std::shared_ptr<element> el; + position pos; + element_float float_side; + element_clear clear_floats; + std::shared_ptr<render_item> el; + int context; floated_box() = default; floated_box(const floated_box& val) @@ -533,6 +606,7 @@ namespace litehtml float_side = val.float_side; clear_floats = val.clear_floats; el = val.el; + context = val.context; } floated_box& operator=(const floated_box& val) { @@ -540,6 +614,7 @@ namespace litehtml float_side = val.float_side; clear_floats = val.clear_floats; el = val.el; + context = val.context; return *this; } floated_box(floated_box&& val) @@ -548,6 +623,7 @@ namespace litehtml float_side = val.float_side; clear_floats = val.clear_floats; el = std::move(val.el); + context = val.context; } void operator=(floated_box&& val) { @@ -555,6 +631,7 @@ namespace litehtml float_side = val.float_side; clear_floats = val.clear_floats; el = std::move(val.el); + context = val.context; } }; @@ -627,7 +704,7 @@ namespace litehtml }; -#define media_orientation_strings _t("portrait;landscape") +#define media_orientation_strings "portrait;landscape" enum media_orientation { @@ -635,7 +712,7 @@ namespace litehtml media_orientation_landscape, }; -#define media_feature_strings _t("none;width;min-width;max-width;height;min-height;max-height;device-width;min-device-width;max-device-width;device-height;min-device-height;max-device-height;orientation;aspect-ratio;min-aspect-ratio;max-aspect-ratio;device-aspect-ratio;min-device-aspect-ratio;max-device-aspect-ratio;color;min-color;max-color;color-index;min-color-index;max-color-index;monochrome;min-monochrome;max-monochrome;resolution;min-resolution;max-resolution") +#define media_feature_strings "none;width;min-width;max-width;height;min-height;max-height;device-width;min-device-width;max-device-width;device-height;min-device-height;max-device-height;orientation;aspect-ratio;min-aspect-ratio;max-aspect-ratio;device-aspect-ratio;min-device-aspect-ratio;max-device-aspect-ratio;color;min-color;max-color;color-index;min-color-index;max-color-index;monochrome;min-monochrome;max-monochrome;resolution;min-resolution;max-resolution" enum media_feature { @@ -684,7 +761,7 @@ namespace litehtml media_feature_max_resolution, }; -#define box_sizing_strings _t("content-box;border-box") +#define box_sizing_strings "content-box;border-box" enum box_sizing { @@ -693,7 +770,7 @@ namespace litehtml }; -#define media_type_strings _t("none;all;screen;print;braille;embossed;handheld;projection;speech;tty;tv") +#define media_type_strings "none;all;screen;print;braille;embossed;handheld;projection;speech;tty;tv" enum media_type { @@ -721,6 +798,19 @@ namespace litehtml int color_index; // The number of entries in the color lookup table of the output device. If the device does not use a color lookup table, the value is zero. int monochrome; // The number of bits per pixel in a monochrome frame buffer. If the device is not a monochrome device, the output device value will be 0. int resolution; // The resolution of the output device (in DPI) + + media_features() + { + type = media_type::media_type_none, + width =0; + height = 0; + device_width = 0; + device_height = 0; + color = 0; + color_index = 0; + monochrome = 0; + resolution = 0; + } }; enum render_type @@ -731,7 +821,84 @@ namespace litehtml }; // List of the Void Elements (can't have any contents) - const litehtml::tchar_t* const void_elements = _t("area;base;br;col;command;embed;hr;img;input;keygen;link;meta;param;source;track;wbr"); + const char* const void_elements = "area;base;br;col;command;embed;hr;img;input;keygen;link;meta;param;source;track;wbr"; + +#define flex_direction_strings "row;row-reverse;column;column-reverse" + + enum flex_direction + { + flex_direction_row, + flex_direction_row_reverse, + flex_direction_column, + flex_direction_column_reverse + }; + +#define flex_wrap_strings "nowrap;wrap;wrap-reverse" + + enum flex_wrap + { + flex_wrap_nowrap, + flex_wrap_wrap, + flex_wrap_wrap_reverse + }; + +#define flex_justify_content_strings "flex-start;flex-end;center;space-between;space-around" + + enum flex_justify_content + { + flex_justify_content_flex_start, + flex_justify_content_flex_end, + flex_justify_content_center, + flex_justify_content_space_between, + flex_justify_content_space_around + }; + +#define flex_align_items_strings "flex-start;flex-end;center;baseline;stretch" + + enum flex_align_items + { + flex_align_items_flex_start, + flex_align_items_flex_end, + flex_align_items_center, + 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 + }; + +#define flex_align_content_strings "flex-start;flex-end;center;space-between;space-around;stretch" + + enum flex_align_content + { + flex_align_content_flex_start, + flex_align_content_flex_end, + flex_align_content_center, + flex_align_content_space_between, + flex_align_content_space_around, + flex_align_content_stretch + }; + +#define flex_basis_strings "auto;content;fit-content;min-content;max-content" + + enum flex_basis + { + flex_basis_auto, + flex_basis_content, + flex_basis_fit_content, + flex_basis_min_content, + flex_basis_max_content, + }; + } #endif // LH_TYPES_H diff --git a/src/plugins/litehtml_viewer/litehtml/url.cpp b/src/plugins/litehtml_viewer/litehtml/url.cpp @@ -0,0 +1,163 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "url.h" + +#include <iostream> +#include <sstream> +#include <algorithm> + +#include "codepoint.h" +#include "url_path.h" + +namespace litehtml { + +url::url(const string& str) +: str_(str) +{ + // TODO: Rewrite using tstring_view to avoid unnecessary allocations. + string tmp = str_; + + // Does the URL include a scheme? + size_t offset = tmp.find(':'); + if (offset != string::npos) { + bool valid_scheme = true; + for (size_t i = 0; i < offset; i++) { + if (!is_url_scheme_codepoint(tmp[i])) { + valid_scheme = false; + break; + } + } + if (valid_scheme) { + scheme_ = tmp.substr(0, offset); + tmp = tmp.substr(offset + 1); + } + } + + // Does the URL include an authority? An authority component is preceded + // by a double slash ("//") and is terminated by the next slash ("/"), + // question mark ("?"), number sign ("#"), or the end of the URL. + + if (tmp.size() >= 2 && tmp[0] == '/' && tmp[1] == '/') { + tmp = tmp.substr(2); + offset = tmp.size(); + offset = std::min(offset, tmp.find('/')); + offset = std::min(offset, tmp.find('?')); + offset = std::min(offset, tmp.find('#')); + authority_ = tmp.substr(0, offset); + tmp = tmp.substr(offset); + + // TODO: Parse the network location into host and port? + } + + // Does the URL include a fragment? + offset = tmp.find('#'); + if (offset != string::npos) { + fragment_ = tmp.substr(offset + 1); + tmp = tmp.substr(0, offset); + } + + // Does the URL include a query? + offset = tmp.find('?'); + if (offset != string::npos) { + query_ = tmp.substr(offset + 1); + tmp = tmp.substr(0, offset); + } + + // Whatever remains of the URL after removing the scheme, the network + // location, the query, and the fragment is the path. + path_ = tmp; +} + +url::url(const string& scheme, + const string& authority, + const string& path, + const string& query, + const string& fragment) +: scheme_(scheme) +, authority_(authority) +, path_(path) +, query_(query) +, fragment_(fragment) +{ + std::stringstream tss; + + if (!scheme_.empty()) { + tss << scheme_ << ":"; + } + if (!authority_.empty()) { + tss << "//" << authority_; + } + if (!path_.empty()) { + tss << path_; + } + if (!query_.empty()) { + tss << "?" << query_; + } + if (!fragment_.empty()) { + tss << "#" << fragment_; + } + str_ = tss.str(); +} + +url resolve(const url& b, const url& r) +{ + // The resolution algorithm roughly follows the resolution algorithm + // outlined in Section 5.2 (in particular Section 5.2.2) of RFC 3986. The + // major difference between the resolution algorithm and resolve() is that + // resolve() does not attempt to normalize the path components. + + if (r.has_scheme()) { + return r; + } else if (r.has_authority()) { + return url(b.scheme(), r.authority(), r.path(), r.query(), r.fragment()); + } else if (r.has_path()) { + + // The relative URL path is either an absolute path or a relative + // path. If it is an absolute path, build the URL using only the + // relative path. If it is a relative path, resolve the relative path + // against the base path and build the URL using the resolved path. + + if (is_url_path_absolute(r.path())) { + return url(b.scheme(), b.authority(), r.path(), r.query(), r.fragment()); + } else { + string path = url_path_resolve(b.path(), r.path()); + return url(b.scheme(), b.authority(), path, r.query(), r.fragment()); + } + + } else if (r.has_query()) { + return url(b.scheme(), b.authority(), b.path(), r.query(), r.fragment()); + } else { + // The resolved URL never includes the base URL fragment (i.e., it + // always includes the reference URL fragment). + return url(b.scheme(), b.authority(), b.path(), b.query(), r.fragment()); + } +} + +} // namespace litehtml diff --git a/src/plugins/litehtml_viewer/litehtml/url.h b/src/plugins/litehtml_viewer/litehtml/url.h @@ -0,0 +1,139 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef LITEHTML_URL_H__ +#define LITEHTML_URL_H__ + +#include <ostream> + +#include "os_types.h" + +// https://datatracker.ietf.org/doc/html/rfc3986 + +namespace litehtml { + +class url { +public: + url() = default; + + explicit url(const string& str); + + url(const string& scheme, + const string& authority, + const string& path, + const string& query, + const string& fragment); + + const string& str() const + { + return str_; + } + + const string& scheme() const + { + return scheme_; + } + + bool has_scheme() const + { + return !scheme_.empty(); + } + + const string& authority() const + { + return authority_; + } + + bool has_authority() const + { + return !authority_.empty(); + } + + const string& path() const + { + return path_; + } + + bool has_path() const + { + return !path_.empty(); + } + + const string& query() const + { + return query_; + } + + bool has_query() const + { + return !query_.empty(); + } + + const string& fragment() const + { + return fragment_; + } + + bool has_fragment() const + { + return !fragment_.empty(); + } + +protected: + string str_; + + // Assume URLs are relative by default. See RFC 3986 Section 4.3 for + // information on which URLs are considered relative and which URLs are + // considered absolute: + // + // https://datatracker.ietf.org/doc/html/rfc3986#section-4.3 + + bool absolute_ = false; + + string scheme_; + + string authority_; + + string path_; + + string query_; + + string fragment_; +}; + +// Returns a URL that is resolved from the reference URL that might be +// relative to the base URL. For example, given <https://www.twitter.com/> as +// the base URL and </foo> as the relative URL, resolve() will return the URL +// <https://www.twitter.com/foo>. + +url resolve(const url& base, const url& reference); + +} // namespace litehtml + +#endif // LITEHTML_URL_H__ diff --git a/src/plugins/litehtml_viewer/litehtml/url_path.cpp b/src/plugins/litehtml_viewer/litehtml/url_path.cpp @@ -0,0 +1,86 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "url_path.h" + +namespace litehtml { + +bool is_url_path_absolute(const string& path) +{ + return path.length() > 0 && path[0] == '/'; +} + +string url_path_directory_name(const string& path) +{ + size_t offset = path.find_last_of('/'); + if (offset == string::npos) { + return "."; + } else { + return path.substr(0, offset + 1); + } +} + +string url_path_base_name(const string& path) +{ + size_t offset = path.find_last_of('/'); + if (offset == string::npos) { + return path; + } else { + return path.substr(offset + 1); + } +} + +string url_path_append(const string& base, const string& path) +{ + string result(base); + + // Only append a separator if both base and path are not empty and if the + // last character of base is not already a separator. + if (!result.empty() && !path.empty() && result.back() != '/') { + result.append(1, '/'); + } + + result.append(path); + + return result; +} + +string url_path_resolve(const string& base, const string& path) +{ + + // If the possibly relative path is an absolute path then it is not + // relative and the base path is irrelevant. + if (is_url_path_absolute(path)) { + return path; + } + + return url_path_append(url_path_directory_name(base), path); +} + +} // namespace litehtml diff --git a/src/plugins/litehtml_viewer/litehtml/url_path.h b/src/plugins/litehtml_viewer/litehtml/url_path.h @@ -0,0 +1,51 @@ +// Copyright (C) 2020-2021 Primate Labs Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the names of the copyright holders nor the names of their +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef LITEHTML_URL_PATH_H__ +#define LITEHTML_URL_PATH_H__ + +#include <ostream> + +#include "os_types.h" + +namespace litehtml { + +bool is_url_path_absolute(const string& path); + +string url_path_directory_name(const string& path); + +string url_path_base_name(const string& path); + +string url_path_append(const string& base, const string& path); + +string url_path_resolve(const string& base, const string& path); + +} // namespace litehtml + +#endif // LITEHTML_URL_PATH_H__ diff --git a/src/plugins/litehtml_viewer/litehtml/utf8_strings.cpp b/src/plugins/litehtml_viewer/litehtml/utf8_strings.cpp @@ -5,6 +5,8 @@ litehtml::utf8_to_wchar::utf8_to_wchar(const char* val) { m_utf8 = (const byte*) val; + if (!m_utf8) return; + while (true) { ucode_t wch = get_char(); diff --git a/src/plugins/litehtml_viewer/litehtml/utf8_strings.h b/src/plugins/litehtml_viewer/litehtml/utf8_strings.h @@ -1,6 +1,9 @@ #ifndef LH_UTF8_STRINGS_H #define LH_UTF8_STRINGS_H +#include "os_types.h" +#include "types.h" + namespace litehtml { class utf8_to_wchar @@ -42,15 +45,8 @@ namespace litehtml } }; -#ifdef LITEHTML_UTF8 -#define litehtml_from_utf8(str) str -#define litehtml_to_utf8(str) str #define litehtml_from_wchar(str) litehtml::wchar_to_utf8(str) -#else -#define litehtml_from_utf8(str) litehtml::utf8_to_wchar(str) -#define litehtml_from_wchar(str) str -#define litehtml_to_utf8(str) litehtml::wchar_to_utf8(str) -#endif +#define litehtml_to_wchar(str) litehtml::utf8_to_wchar(str) } #endif // LH_UTF8_STRINGS_H diff --git a/src/plugins/litehtml_viewer/litehtml/web_color.cpp b/src/plugins/litehtml_viewer/litehtml/web_color.cpp @@ -1,171 +1,176 @@ #include "html.h" #include "web_color.h" -#include <string.h> +#include <cstring> + +const litehtml::web_color litehtml::web_color::transparent = web_color(0, 0, 0, 0); +const litehtml::web_color litehtml::web_color::black = web_color(0, 0, 0, 255); +const litehtml::web_color litehtml::web_color::white = web_color(255, 255, 255, 255); litehtml::def_color litehtml::g_def_colors[] = { - {_t("transparent"),_t("rgba(0, 0, 0, 0)")}, - {_t("AliceBlue"),_t("#F0F8FF")}, - {_t("AntiqueWhite"),_t("#FAEBD7")}, - {_t("Aqua"),_t("#00FFFF")}, - {_t("Aquamarine"),_t("#7FFFD4")}, - {_t("Azure"),_t("#F0FFFF")}, - {_t("Beige"),_t("#F5F5DC")}, - {_t("Bisque"),_t("#FFE4C4")}, - {_t("Black"),_t("#000000")}, - {_t("BlanchedAlmond"),_t("#FFEBCD")}, - {_t("Blue"),_t("#0000FF")}, - {_t("BlueViolet"),_t("#8A2BE2")}, - {_t("Brown"),_t("#A52A2A")}, - {_t("BurlyWood"),_t("#DEB887")}, - {_t("CadetBlue"),_t("#5F9EA0")}, - {_t("Chartreuse"),_t("#7FFF00")}, - {_t("Chocolate"),_t("#D2691E")}, - {_t("Coral"),_t("#FF7F50")}, - {_t("CornflowerBlue"),_t("#6495ED")}, - {_t("Cornsilk"),_t("#FFF8DC")}, - {_t("Crimson"),_t("#DC143C")}, - {_t("Cyan"),_t("#00FFFF")}, - {_t("DarkBlue"),_t("#00008B")}, - {_t("DarkCyan"),_t("#008B8B")}, - {_t("DarkGoldenRod"),_t("#B8860B")}, - {_t("DarkGray"),_t("#A9A9A9")}, - {_t("DarkGrey"),_t("#A9A9A9")}, - {_t("DarkGreen"),_t("#006400")}, - {_t("DarkKhaki"),_t("#BDB76B")}, - {_t("DarkMagenta"),_t("#8B008B")}, - {_t("DarkOliveGreen"),_t("#556B2F")}, - {_t("Darkorange"),_t("#FF8C00")}, - {_t("DarkOrchid"),_t("#9932CC")}, - {_t("DarkRed"),_t("#8B0000")}, - {_t("DarkSalmon"),_t("#E9967A")}, - {_t("DarkSeaGreen"),_t("#8FBC8F")}, - {_t("DarkSlateBlue"),_t("#483D8B")}, - {_t("DarkSlateGray"),_t("#2F4F4F")}, - {_t("DarkSlateGrey"),_t("#2F4F4F")}, - {_t("DarkTurquoise"),_t("#00CED1")}, - {_t("DarkViolet"),_t("#9400D3")}, - {_t("DeepPink"),_t("#FF1493")}, - {_t("DeepSkyBlue"),_t("#00BFFF")}, - {_t("DimGray"),_t("#696969")}, - {_t("DimGrey"),_t("#696969")}, - {_t("DodgerBlue"),_t("#1E90FF")}, - {_t("FireBrick"),_t("#B22222")}, - {_t("FloralWhite"),_t("#FFFAF0")}, - {_t("ForestGreen"),_t("#228B22")}, - {_t("Fuchsia"),_t("#FF00FF")}, - {_t("Gainsboro"),_t("#DCDCDC")}, - {_t("GhostWhite"),_t("#F8F8FF")}, - {_t("Gold"),_t("#FFD700")}, - {_t("GoldenRod"),_t("#DAA520")}, - {_t("Gray"),_t("#808080")}, - {_t("Grey"),_t("#808080")}, - {_t("Green"),_t("#008000")}, - {_t("GreenYellow"),_t("#ADFF2F")}, - {_t("HoneyDew"),_t("#F0FFF0")}, - {_t("HotPink"),_t("#FF69B4")}, - {_t("Ivory"),_t("#FFFFF0")}, - {_t("Khaki"),_t("#F0E68C")}, - {_t("Lavender"),_t("#E6E6FA")}, - {_t("LavenderBlush"),_t("#FFF0F5")}, - {_t("LawnGreen"),_t("#7CFC00")}, - {_t("LemonChiffon"),_t("#FFFACD")}, - {_t("LightBlue"),_t("#ADD8E6")}, - {_t("LightCoral"),_t("#F08080")}, - {_t("LightCyan"),_t("#E0FFFF")}, - {_t("LightGoldenRodYellow"),_t("#FAFAD2")}, - {_t("LightGray"),_t("#D3D3D3")}, - {_t("LightGrey"),_t("#D3D3D3")}, - {_t("LightGreen"),_t("#90EE90")}, - {_t("LightPink"),_t("#FFB6C1")}, - {_t("LightSalmon"),_t("#FFA07A")}, - {_t("LightSeaGreen"),_t("#20B2AA")}, - {_t("LightSkyBlue"),_t("#87CEFA")}, - {_t("LightSlateGray"),_t("#778899")}, - {_t("LightSlateGrey"),_t("#778899")}, - {_t("LightSteelBlue"),_t("#B0C4DE")}, - {_t("LightYellow"),_t("#FFFFE0")}, - {_t("Lime"),_t("#00FF00")}, - {_t("LimeGreen"),_t("#32CD32")}, - {_t("Linen"),_t("#FAF0E6")}, - {_t("Magenta"),_t("#FF00FF")}, - {_t("Maroon"),_t("#800000")}, - {_t("MediumAquaMarine"),_t("#66CDAA")}, - {_t("MediumBlue"),_t("#0000CD")}, - {_t("MediumOrchid"),_t("#BA55D3")}, - {_t("MediumPurple"),_t("#9370D8")}, - {_t("MediumSeaGreen"),_t("#3CB371")}, - {_t("MediumSlateBlue"),_t("#7B68EE")}, - {_t("MediumSpringGreen"),_t("#00FA9A")}, - {_t("MediumTurquoise"),_t("#48D1CC")}, - {_t("MediumVioletRed"),_t("#C71585")}, - {_t("MidnightBlue"),_t("#191970")}, - {_t("MintCream"),_t("#F5FFFA")}, - {_t("MistyRose"),_t("#FFE4E1")}, - {_t("Moccasin"),_t("#FFE4B5")}, - {_t("NavajoWhite"),_t("#FFDEAD")}, - {_t("Navy"),_t("#000080")}, - {_t("OldLace"),_t("#FDF5E6")}, - {_t("Olive"),_t("#808000")}, - {_t("OliveDrab"),_t("#6B8E23")}, - {_t("Orange"),_t("#FFA500")}, - {_t("OrangeRed"),_t("#FF4500")}, - {_t("Orchid"),_t("#DA70D6")}, - {_t("PaleGoldenRod"),_t("#EEE8AA")}, - {_t("PaleGreen"),_t("#98FB98")}, - {_t("PaleTurquoise"),_t("#AFEEEE")}, - {_t("PaleVioletRed"),_t("#D87093")}, - {_t("PapayaWhip"),_t("#FFEFD5")}, - {_t("PeachPuff"),_t("#FFDAB9")}, - {_t("Peru"),_t("#CD853F")}, - {_t("Pink"),_t("#FFC0CB")}, - {_t("Plum"),_t("#DDA0DD")}, - {_t("PowderBlue"),_t("#B0E0E6")}, - {_t("Purple"),_t("#800080")}, - {_t("Red"),_t("#FF0000")}, - {_t("RosyBrown"),_t("#BC8F8F")}, - {_t("RoyalBlue"),_t("#4169E1")}, - {_t("SaddleBrown"),_t("#8B4513")}, - {_t("Salmon"),_t("#FA8072")}, - {_t("SandyBrown"),_t("#F4A460")}, - {_t("SeaGreen"),_t("#2E8B57")}, - {_t("SeaShell"),_t("#FFF5EE")}, - {_t("Sienna"),_t("#A0522D")}, - {_t("Silver"),_t("#C0C0C0")}, - {_t("SkyBlue"),_t("#87CEEB")}, - {_t("SlateBlue"),_t("#6A5ACD")}, - {_t("SlateGray"),_t("#708090")}, - {_t("SlateGrey"),_t("#708090")}, - {_t("Snow"),_t("#FFFAFA")}, - {_t("SpringGreen"),_t("#00FF7F")}, - {_t("SteelBlue"),_t("#4682B4")}, - {_t("Tan"),_t("#D2B48C")}, - {_t("Teal"),_t("#008080")}, - {_t("Thistle"),_t("#D8BFD8")}, - {_t("Tomato"),_t("#FF6347")}, - {_t("Turquoise"),_t("#40E0D0")}, - {_t("Violet"),_t("#EE82EE")}, - {_t("Wheat"),_t("#F5DEB3")}, - {_t("White"),_t("#FFFFFF")}, - {_t("WhiteSmoke"),_t("#F5F5F5")}, - {_t("Yellow"),_t("#FFFF00")}, - {_t("YellowGreen"),_t("#9ACD32")}, - {0,0} + {"transparent","rgba(0, 0, 0, 0)"}, + {"AliceBlue","#F0F8FF"}, + {"AntiqueWhite","#FAEBD7"}, + {"Aqua","#00FFFF"}, + {"Aquamarine","#7FFFD4"}, + {"Azure","#F0FFFF"}, + {"Beige","#F5F5DC"}, + {"Bisque","#FFE4C4"}, + {"Black","#000000"}, + {"BlanchedAlmond","#FFEBCD"}, + {"Blue","#0000FF"}, + {"BlueViolet","#8A2BE2"}, + {"Brown","#A52A2A"}, + {"BurlyWood","#DEB887"}, + {"CadetBlue","#5F9EA0"}, + {"Chartreuse","#7FFF00"}, + {"Chocolate","#D2691E"}, + {"Coral","#FF7F50"}, + {"CornflowerBlue","#6495ED"}, + {"Cornsilk","#FFF8DC"}, + {"Crimson","#DC143C"}, + {"Cyan","#00FFFF"}, + {"DarkBlue","#00008B"}, + {"DarkCyan","#008B8B"}, + {"DarkGoldenRod","#B8860B"}, + {"DarkGray","#A9A9A9"}, + {"DarkGrey","#A9A9A9"}, + {"DarkGreen","#006400"}, + {"DarkKhaki","#BDB76B"}, + {"DarkMagenta","#8B008B"}, + {"DarkOliveGreen","#556B2F"}, + {"Darkorange","#FF8C00"}, + {"DarkOrchid","#9932CC"}, + {"DarkRed","#8B0000"}, + {"DarkSalmon","#E9967A"}, + {"DarkSeaGreen","#8FBC8F"}, + {"DarkSlateBlue","#483D8B"}, + {"DarkSlateGray","#2F4F4F"}, + {"DarkSlateGrey","#2F4F4F"}, + {"DarkTurquoise","#00CED1"}, + {"DarkViolet","#9400D3"}, + {"DeepPink","#FF1493"}, + {"DeepSkyBlue","#00BFFF"}, + {"DimGray","#696969"}, + {"DimGrey","#696969"}, + {"DodgerBlue","#1E90FF"}, + {"FireBrick","#B22222"}, + {"FloralWhite","#FFFAF0"}, + {"ForestGreen","#228B22"}, + {"Fuchsia","#FF00FF"}, + {"Gainsboro","#DCDCDC"}, + {"GhostWhite","#F8F8FF"}, + {"Gold","#FFD700"}, + {"GoldenRod","#DAA520"}, + {"Gray","#808080"}, + {"Grey","#808080"}, + {"Green","#008000"}, + {"GreenYellow","#ADFF2F"}, + {"HoneyDew","#F0FFF0"}, + {"HotPink","#FF69B4"}, + {"Ivory","#FFFFF0"}, + {"Khaki","#F0E68C"}, + {"Lavender","#E6E6FA"}, + {"LavenderBlush","#FFF0F5"}, + {"LawnGreen","#7CFC00"}, + {"LemonChiffon","#FFFACD"}, + {"LightBlue","#ADD8E6"}, + {"LightCoral","#F08080"}, + {"LightCyan","#E0FFFF"}, + {"LightGoldenRodYellow","#FAFAD2"}, + {"LightGray","#D3D3D3"}, + {"LightGrey","#D3D3D3"}, + {"LightGreen","#90EE90"}, + {"LightPink","#FFB6C1"}, + {"LightSalmon","#FFA07A"}, + {"LightSeaGreen","#20B2AA"}, + {"LightSkyBlue","#87CEFA"}, + {"LightSlateGray","#778899"}, + {"LightSlateGrey","#778899"}, + {"LightSteelBlue","#B0C4DE"}, + {"LightYellow","#FFFFE0"}, + {"Lime","#00FF00"}, + {"LimeGreen","#32CD32"}, + {"Linen","#FAF0E6"}, + {"Magenta","#FF00FF"}, + {"Maroon","#800000"}, + {"MediumAquaMarine","#66CDAA"}, + {"MediumBlue","#0000CD"}, + {"MediumOrchid","#BA55D3"}, + {"MediumPurple","#9370D8"}, + {"MediumSeaGreen","#3CB371"}, + {"MediumSlateBlue","#7B68EE"}, + {"MediumSpringGreen","#00FA9A"}, + {"MediumTurquoise","#48D1CC"}, + {"MediumVioletRed","#C71585"}, + {"MidnightBlue","#191970"}, + {"MintCream","#F5FFFA"}, + {"MistyRose","#FFE4E1"}, + {"Moccasin","#FFE4B5"}, + {"NavajoWhite","#FFDEAD"}, + {"Navy","#000080"}, + {"OldLace","#FDF5E6"}, + {"Olive","#808000"}, + {"OliveDrab","#6B8E23"}, + {"Orange","#FFA500"}, + {"OrangeRed","#FF4500"}, + {"Orchid","#DA70D6"}, + {"PaleGoldenRod","#EEE8AA"}, + {"PaleGreen","#98FB98"}, + {"PaleTurquoise","#AFEEEE"}, + {"PaleVioletRed","#D87093"}, + {"PapayaWhip","#FFEFD5"}, + {"PeachPuff","#FFDAB9"}, + {"Peru","#CD853F"}, + {"Pink","#FFC0CB"}, + {"Plum","#DDA0DD"}, + {"PowderBlue","#B0E0E6"}, + {"Purple","#800080"}, + {"Red","#FF0000"}, + {"RosyBrown","#BC8F8F"}, + {"RoyalBlue","#4169E1"}, + {"SaddleBrown","#8B4513"}, + {"Salmon","#FA8072"}, + {"SandyBrown","#F4A460"}, + {"SeaGreen","#2E8B57"}, + {"SeaShell","#FFF5EE"}, + {"Sienna","#A0522D"}, + {"Silver","#C0C0C0"}, + {"SkyBlue","#87CEEB"}, + {"SlateBlue","#6A5ACD"}, + {"SlateGray","#708090"}, + {"SlateGrey","#708090"}, + {"Snow","#FFFAFA"}, + {"SpringGreen","#00FF7F"}, + {"SteelBlue","#4682B4"}, + {"Tan","#D2B48C"}, + {"Teal","#008080"}, + {"Thistle","#D8BFD8"}, + {"Tomato","#FF6347"}, + {"Turquoise","#40E0D0"}, + {"Violet","#EE82EE"}, + {"Wheat","#F5DEB3"}, + {"White","#FFFFFF"}, + {"WhiteSmoke","#F5F5F5"}, + {"Yellow","#FFFF00"}, + {"YellowGreen","#9ACD32"}, + {nullptr,nullptr} }; -litehtml::web_color litehtml::web_color::from_string(const tchar_t* str, litehtml::document_container* callback) +litehtml::web_color litehtml::web_color::from_string(const string& _str, document_container* callback) { - if(!str || !str[0]) + auto str = _str.c_str(); + if(!str[0]) { return web_color(0, 0, 0); } - if(str[0] == _t('#')) + if(str[0] == '#') { - tstring red = _t(""); - tstring green = _t(""); - tstring blue = _t(""); - if(t_strlen(str + 1) == 3) + string red; + string green; + string blue; + if(strlen(str + 1) == 3) { red += str[1]; red += str[1]; @@ -173,7 +178,7 @@ litehtml::web_color litehtml::web_color::from_string(const tchar_t* str, litehtm green += str[2]; blue += str[3]; blue += str[3]; - } else if(t_strlen(str + 1) == 6) + } else if(strlen(str + 1) == 6) { red += str[1]; red += str[2]; @@ -182,41 +187,41 @@ litehtml::web_color litehtml::web_color::from_string(const tchar_t* str, litehtm blue += str[5]; blue += str[6]; } - tchar_t* sss = 0; + char* sss = nullptr; web_color clr; - clr.red = (byte) t_strtol(red.c_str(), &sss, 16); - clr.green = (byte) t_strtol(green.c_str(), &sss, 16); - clr.blue = (byte) t_strtol(blue.c_str(), &sss, 16); + clr.red = (byte) strtol(red.c_str(), &sss, 16); + clr.green = (byte) strtol(green.c_str(), &sss, 16); + clr.blue = (byte) strtol(blue.c_str(), &sss, 16); return clr; - } else if(!t_strncmp(str, _t("rgb"), 3)) + } else if(!strncmp(str, "rgb", 3)) { - tstring s = str; + string s = str; - tstring::size_type pos = s.find_first_of(_t("(")); - if(pos != tstring::npos) + string::size_type pos = s.find_first_of('('); + if(pos != string::npos) { s.erase(s.begin(), s.begin() + pos + 1); } - pos = s.find_last_of(_t(")")); - if(pos != tstring::npos) + pos = s.find_last_of(')'); + if(pos != string::npos) { s.erase(s.begin() + pos, s.end()); } - std::vector<tstring> tokens; - split_string(s, tokens, _t(", \t")); + std::vector<string> tokens; + split_string(s, tokens, ", \t"); web_color clr; - if(tokens.size() >= 1) clr.red = (byte) t_atoi(tokens[0].c_str()); - if(tokens.size() >= 2) clr.green = (byte) t_atoi(tokens[1].c_str()); - if(tokens.size() >= 3) clr.blue = (byte) t_atoi(tokens[2].c_str()); - if(tokens.size() >= 4) clr.alpha = (byte) (t_strtod(tokens[3].c_str(), 0) * 255.0); + if(tokens.size() >= 1) clr.red = (byte) atoi(tokens[0].c_str()); + if(tokens.size() >= 2) clr.green = (byte) atoi(tokens[1].c_str()); + if(tokens.size() >= 3) clr.blue = (byte) atoi(tokens[2].c_str()); + if(tokens.size() >= 4) clr.alpha = (byte) (t_strtod(tokens[3].c_str(), nullptr) * 255.0); return clr; } else { - tstring rgb = resolve_name(str, callback); + string rgb = resolve_name(str, callback); if(!rgb.empty()) { return from_string(rgb.c_str(), callback); @@ -225,32 +230,45 @@ litehtml::web_color litehtml::web_color::from_string(const tchar_t* str, litehtm return web_color(0, 0, 0); } -litehtml::tstring litehtml::web_color::resolve_name(const tchar_t* name, litehtml::document_container* callback) +litehtml::string litehtml::web_color::resolve_name(const string& name, document_container* callback) { for(int i=0; g_def_colors[i].name; i++) { - if(!t_strcasecmp(name, g_def_colors[i].name)) + if(!t_strcasecmp(name.c_str(), g_def_colors[i].name)) { - return std::move(litehtml::tstring(g_def_colors[i].rgb)); + return g_def_colors[i].rgb; } } if (callback) { - litehtml::tstring clr = callback->resolve_color(name); - return std::move(clr); + string clr = callback->resolve_color(name); + return clr; } - return std::move(litehtml::tstring()); + return ""; } -bool litehtml::web_color::is_color(const tchar_t* str) +bool litehtml::web_color::is_color(const string& str, document_container* callback) { - if(!t_strncasecmp(str, _t("rgb"), 3) || str[0] == _t('#')) + if (!t_strncasecmp(str.c_str(), "rgb", 3) || str[0] == '#') { return true; } - if (!t_isdigit(str[0]) && str[0] != _t('.')) + if (t_isalpha(str[0]) && resolve_name(str, callback) != "") { return true; } return false; } + +litehtml::string litehtml::web_color::to_string() const +{ + char str[9]; + if(alpha) + { + t_snprintf(str, 9, "%02X%02X%02X%02X", red, green, blue, alpha); + } else + { + t_snprintf(str, 9, "%02X%02X%02X", red, green, blue); + } + return str; +} diff --git a/src/plugins/litehtml_viewer/litehtml/web_color.h b/src/plugins/litehtml_viewer/litehtml/web_color.h @@ -5,56 +5,48 @@ namespace litehtml { struct def_color { - const tchar_t* name; - const tchar_t* rgb; + const char* name; + const char* rgb; }; extern def_color g_def_colors[]; - class document_container; + class document_container; struct web_color { - byte blue; - byte green; - byte red; - byte alpha; + byte red; + byte green; + byte blue; + byte alpha; + + static const web_color transparent; + static const web_color black; + static const web_color white; web_color(byte r, byte g, byte b, byte a = 255) { - blue = b; - green = g; red = r; + green = g; + blue = b; alpha = a; } web_color() { - blue = 0; - green = 0; red = 0; + green = 0; + blue = 0; alpha = 0xFF; } - web_color(const web_color& val) - { - blue = val.blue; - green = val.green; - red = val.red; - alpha = val.alpha; - } + bool operator==(web_color color) const { return red == color.red && green == color.green && blue == color.blue && alpha == color.alpha; } + bool operator!=(web_color color) const { return !(*this == color); } - web_color& operator=(const web_color& val) - { - blue = val.blue; - green = val.green; - red = val.red; - alpha = val.alpha; - return *this; - } - static web_color from_string(const tchar_t* str, litehtml::document_container* callback); - static litehtml::tstring resolve_name(const tchar_t* name, litehtml::document_container* callback); - static bool is_color(const tchar_t* str); + string to_string() const; + static web_color from_string(const string& str, document_container* callback); + static string resolve_name(const string& name, document_container* callback); + static bool is_color(const string& str, document_container* callback); }; }