2#pragma warning(disable: 4100)
3#pragma warning(disable: 4244)
4#elif defined(__clang__)
5#pragma clang diagnostic ignored "-Wunused-parameter"
6#pragma clang diagnostic ignored "-Wmissing-field-initializers"
12#include "stylesheet.h"
25#include "el_comment.h"
37#include "utf8_strings.h"
41 m_container = objContainer;
44 m_initialised =
false;
47litehtml::document::~document()
52 for(fonts_map::iterator f = m_fonts.begin(); f != m_fonts.end(); f++)
54 m_container->delete_font(f->second.font);
56 m_container =
nullptr;
60void litehtml::document::apply_styles(
const litehtml::css* styles) {
61 if (m_root && styles) {
62 m_root->apply_stylesheet(*styles);
63 m_root->parse_styles();
69 return createFromUTF8(litehtml_to_utf8(str), objPainter, ctx, user_styles);
78 litehtml::document::ptr doc = std::make_shared<litehtml::document>(objPainter, ctx);
81 elements_vector root_elements;
82 doc->create_node(output->root, root_elements,
true, doc);
83 if (!root_elements.empty())
85 doc->m_root = root_elements.back();
93 doc->container()->get_media_features(doc->m_media);
96 doc->m_root->apply_stylesheet(ctx->master_css());
99 doc->m_root->parse_attributes();
102 media_query_list::ptr media;
103 for (css_text::vector::iterator css = doc->m_css.begin(); css != doc->m_css.end(); css++)
105 if (!css->media.empty())
107 media = media_query_list::create_from_string(css->media, doc);
113 doc->m_styles.parse_stylesheet(css->text.c_str(), css->baseurl.c_str(), doc, media);
116 doc->m_styles.sort_selectors();
119 if (!doc->m_media_lists.empty())
121 doc->update_media_lists(doc->m_media);
125 doc->m_root->apply_stylesheet(doc->m_styles);
130 doc->m_root->apply_stylesheet(*user_styles);
134 doc->m_root->parse_styles();
139 doc->fix_tables_layout();
144 doc->m_initialised =
true;
149litehtml::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 )
153 if( !name || (name && !t_strcasecmp(name, _t(
"inherit"))) )
155 name = m_container->get_default_font_name();
160 size = container()->get_default_font_size();
164 t_itoa(size, strSize, 20, 10);
176 if(m_fonts.find(key) == m_fonts.end())
178 font_style fs = (font_style) value_index(style, font_style_strings, fontStyleNormal);
179 int fw = value_index(weight, font_weight_strings, -1);
184 case litehtml::fontWeightBold:
187 case litehtml::fontWeightBolder:
190 case litehtml::fontWeightLighter:
206 unsigned int decor = 0;
210 std::vector<tstring> tokens;
211 split_string(decoration, tokens, _t(
" "));
212 for(std::vector<tstring>::iterator i = tokens.begin(); i != tokens.end(); i++)
214 if(!t_strcasecmp(i->c_str(), _t(
"underline")))
216 decor |= font_decoration_underline;
217 }
else if(!t_strcasecmp(i->c_str(), _t(
"line-through")))
219 decor |= font_decoration_linethrough;
220 }
else if(!t_strcasecmp(i->c_str(), _t(
"overline")))
222 decor |= font_decoration_overline;
229 fi.font = m_container->create_font(name, size, fw, fs, decor, &fi.metrics);
240litehtml::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 )
242 if( !name || (name && !t_strcasecmp(name, _t(
"inherit"))) )
244 name = m_container->get_default_font_name();
249 size = m_container->get_default_font_size();
253 t_itoa(size, strSize, 20, 10);
265 fonts_map::iterator el = m_fonts.find(key);
267 if(el != m_fonts.end())
271 *fm = el->second.metrics;
273 return el->second.font;
275 return add_font(name, size, weight, style, decoration, fm);
278int litehtml::document::render(
int max_width, render_type rt )
283 if(rt == render_fixed_only)
285 m_fixed_boxes.clear();
286 m_root->render_positioned(rt);
289 ret = m_root->render(0, 0, max_width);
290 if(m_root->fetch_positioned())
292 m_fixed_boxes.clear();
293 m_root->render_positioned(rt);
297 m_root->calc_document_size(m_size);
303void litehtml::document::draw( uint_ptr hdc,
int x,
int y,
const position* clip )
307 m_root->draw(hdc, x, y, clip);
308 m_root->draw_stacking_context(hdc, x, y, clip,
true);
312int litehtml::document::cvt_units(
const tchar_t* str,
int fontSize,
bool* is_percent )
const
318 if(is_percent && val.units() == css_units_percentage && !val.is_predefined())
322 return cvt_units(val, fontSize);
325int litehtml::document::cvt_units( css_length& val,
int fontSize,
int size )
const
327 if(val.is_predefined())
334 case css_units_percentage:
335 ret = val.calc_percent(size);
338 ret = round_f(val.val() * fontSize);
339 val.set_value((
float) ret, css_units_px);
342 ret = m_container->pt_to_px((
int) val.val());
343 val.set_value((
float) ret, css_units_px);
346 ret = m_container->pt_to_px((
int) (val.val() * 72));
347 val.set_value((
float) ret, css_units_px);
350 ret = m_container->pt_to_px((
int) (val.val() * 0.3937 * 72));
351 val.set_value((
float) ret, css_units_px);
354 ret = m_container->pt_to_px((
int) (val.val() * 0.3937 * 72) / 10);
355 val.set_value((
float) ret, css_units_px);
358 ret = (int)((
double)m_media.width * (double)val.val() / 100.0);
361 ret = (int)((
double)m_media.height * (double)val.val() / 100.0);
364 ret = (int)((
double)std::min(m_media.height, m_media.width) * (double)val.val() / 100.0);
367 ret = (int)((
double)std::max(m_media.height, m_media.width) * (double)val.val() / 100.0);
370 ret = (int) ((
double) m_root->get_font_size() * (double) val.val());
371 val.set_value((
float) ret, css_units_px);
374 ret = (int) val.val();
380int litehtml::document::width()
const
385int litehtml::document::height()
const
387 return m_size.height;
390void litehtml::document::add_stylesheet(
const tchar_t* str,
const tchar_t* baseurl,
const tchar_t* media )
394 m_css.push_back(css_text(str, baseurl, media));
398bool litehtml::document::on_mouse_move(
int x,
int y)
407 m_container->on_mouse_move(m_over_element, x, y);
415bool litehtml::document::on_mouse_over(
int x,
int y,
int client_x,
int client_y, position::vector& redraw_boxes )
422 element::ptr over_el = m_root->get_element_by_point(x, y, client_x, client_y);
424 bool state_was_changed =
false;
426 if(over_el != m_over_element)
430 if(m_over_element->on_mouse_leave())
432 state_was_changed =
true;
433 m_container->on_mouse_leave(m_over_element);
436 m_over_element = over_el;
439 const tchar_t* cursor = 0;
443 if(m_over_element->on_mouse_over())
445 state_was_changed =
true;
446 m_container->on_mouse_enter(m_over_element);
448 cursor = m_over_element->get_cursor();
451 m_container->set_cursor(cursor ? cursor : _t(
"auto"));
453 if(state_was_changed)
455 return m_root->find_styles_changes(redraw_boxes, 0, 0);
460bool litehtml::document::on_mouse_leave( position::vector& redraw_boxes )
468 if(m_over_element->on_mouse_leave())
470 m_container->on_mouse_leave(m_over_element);
471 return m_root->find_styles_changes(redraw_boxes, 0, 0);
477bool litehtml::document::on_lbutton_down(
int x,
int y,
int client_x,
int client_y, position::vector& redraw_boxes )
484 element::ptr over_el = m_root->get_element_by_point(x, y, client_x, client_y);
486 bool state_was_changed =
false;
488 if(over_el != m_over_element)
492 if(m_over_element->on_mouse_leave())
494 state_was_changed =
true;
495 m_container->on_mouse_leave(m_over_element);
498 m_over_element = over_el;
501 if(m_over_element->on_mouse_over())
503 state_was_changed =
true;
504 m_container->on_mouse_enter(m_over_element);
509 const tchar_t* cursor = 0;
513 if(m_over_element->on_lbutton_down())
515 state_was_changed =
true;
517 cursor = m_over_element->get_cursor();
520 m_container->set_cursor(cursor ? cursor : _t(
"auto"));
522 if(state_was_changed)
524 return m_root->find_styles_changes(redraw_boxes, 0, 0);
530bool litehtml::document::on_lbutton_up(
int x,
int y,
int client_x,
int client_y, position::vector& redraw_boxes )
538 if(m_over_element->on_lbutton_up())
540 return m_root->find_styles_changes(redraw_boxes, 0, 0);
546litehtml::element::ptr litehtml::document::create_element(
const tchar_t* tag_name,
const string_map& attributes)
549 document::ptr this_doc = shared_from_this();
552 newTag = m_container->create_element(tag_name, attributes, this_doc);
556 if(!t_strcmp(tag_name, _t(
"br")))
558 newTag = std::make_shared<litehtml::el_break>(this_doc);
559 }
else if(!t_strcmp(tag_name, _t(
"p")))
561 newTag = std::make_shared<litehtml::el_para>(this_doc);
562 }
else if(!t_strcmp(tag_name, _t(
"img")))
564 newTag = std::make_shared<litehtml::el_image>(this_doc);
565 }
else if(!t_strcmp(tag_name, _t(
"table")))
567 newTag = std::make_shared<litehtml::el_table>(this_doc);
568 }
else if(!t_strcmp(tag_name, _t(
"td")) || !t_strcmp(tag_name, _t(
"th")))
570 newTag = std::make_shared<litehtml::el_td>(this_doc);
571 }
else if(!t_strcmp(tag_name, _t(
"link")))
573 newTag = std::make_shared<litehtml::el_link>(this_doc);
574 }
else if(!t_strcmp(tag_name, _t(
"title")))
576 newTag = std::make_shared<litehtml::el_title>(this_doc);
577 }
else if(!t_strcmp(tag_name, _t(
"a")))
579 newTag = std::make_shared<litehtml::el_anchor>(this_doc);
580 }
else if(!t_strcmp(tag_name, _t(
"tr")))
582 newTag = std::make_shared<litehtml::el_tr>(this_doc);
583 }
else if(!t_strcmp(tag_name, _t(
"style")))
585 newTag = std::make_shared<litehtml::el_style>(this_doc);
586 }
else if(!t_strcmp(tag_name, _t(
"base")))
588 newTag = std::make_shared<litehtml::el_base>(this_doc);
589 }
else if(!t_strcmp(tag_name, _t(
"body")))
591 newTag = std::make_shared<litehtml::el_body>(this_doc);
592 }
else if(!t_strcmp(tag_name, _t(
"div")))
594 newTag = std::make_shared<litehtml::el_div>(this_doc);
595 }
else if(!t_strcmp(tag_name, _t(
"script")))
597 newTag = std::make_shared<litehtml::el_script>(this_doc);
598 }
else if(!t_strcmp(tag_name, _t(
"font")))
600 newTag = std::make_shared<litehtml::el_font>(this_doc);
603 newTag = std::make_shared<litehtml::html_tag>(this_doc);
609 newTag->set_tagName(tag_name);
610 for (string_map::const_iterator iter = attributes.begin(); iter != attributes.end(); iter++)
612 newTag->set_attr(iter->first.c_str(), iter->second.c_str());
619void litehtml::document::get_fixed_boxes( position::vector& fixed_boxes )
621 fixed_boxes = m_fixed_boxes;
624void litehtml::document::add_fixed_box(
const position& pos )
626 m_fixed_boxes.push_back(pos);
629bool litehtml::document::media_changed()
631 if(!m_media_lists.empty())
633 container()->get_media_features(m_media);
634 if (update_media_lists(m_media))
636 m_root->refresh_styles();
637 m_root->parse_styles();
644bool litehtml::document::lang_changed()
646 if(!m_media_lists.empty())
649 container()->get_language(m_lang, culture);
652 m_culture = m_lang + _t(
'-') + culture;
658 m_root->refresh_styles();
659 m_root->parse_styles();
665bool litehtml::document::update_media_lists(
const media_features& features)
667 bool update_styles =
false;
668 for(media_query_list::vector::iterator iter = m_media_lists.begin(); iter != m_media_lists.end(); iter++)
670 if((*iter)->apply_media_features(features))
672 update_styles =
true;
675 return update_styles;
678void litehtml::document::add_media_list( media_query_list::ptr list )
682 if(std::find(m_media_lists.begin(), m_media_lists.end(), list) == m_media_lists.end())
684 m_media_lists.push_back(list);
689void litehtml::document::create_node(
void* gnode, elements_vector& elements,
bool parseTextNode, document::ptr doc)
701 attrs[tstring(litehtml_from_utf8(attr->
name))] = litehtml_from_utf8(attr->
value);
709 ret = doc->create_element(litehtml_from_utf8(tag), attrs);
718 ret = doc->create_element(litehtml_from_utf8(strA.c_str()), attrs);
721 if (!strcmp(tag,
"script"))
723 parseTextNode =
false;
727 elements_vector child;
732 std::for_each(child.begin(), child.end(),
733 [&ret](element::ptr& el)
735 ret->appendChild(el);
739 elements.push_back(ret);
746 std::wstring str_in = (
const wchar_t*) (utf8_to_wchar(node->
v.text.
text));
749 elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str_in.c_str()), doc));
753 for (
size_t i = 0; i < str_in.length(); i++)
755 c = (ucode_t) str_in[i];
756 if (c <=
' ' && (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
'\f'))
760 elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), doc));
764 elements.push_back(std::make_shared<el_space>(litehtml_from_wchar(str.c_str()), doc));
768 else if (c >= 0x4E00 && c <= 0x9FCC)
772 elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), doc));
776 elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), doc));
786 elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), doc));
792 element::ptr ret = std::make_shared<el_cdata>(doc);
793 ret->set_data(litehtml_from_utf8(node->
v.text.
text));
794 elements.push_back(ret);
799 element::ptr ret = std::make_shared<el_comment>(doc);
800 ret->set_data(litehtml_from_utf8(node->
v.text.
text));
801 elements.push_back(ret);
806 tstring str = litehtml_from_utf8(node->
v.text.
text);
807 for (
size_t i = 0; i < str.length(); i++)
809 elements.push_back(std::make_shared<el_space>(str.substr(i, 1).c_str(), doc));
818void litehtml::document::fix_tables_layout()
821 while (i < m_tabular_elements.size())
823 element::ptr el_ptr = m_tabular_elements[i];
825 switch (el_ptr->get_display())
827 case display_inline_table:
829 fix_table_children(el_ptr, display_table_row_group, _t(
"table-row-group"));
831 case display_table_footer_group:
832 case display_table_row_group:
833 case display_table_header_group:
835 element::ptr parent = el_ptr->parent();
838 if (parent->get_display() != display_inline_table)
839 fix_table_parent(el_ptr, display_table, _t(
"table"));
841 fix_table_children(el_ptr, display_table_row, _t(
"table-row"));
844 case display_table_row:
845 fix_table_parent(el_ptr, display_table_row_group, _t(
"table-row-group"));
846 fix_table_children(el_ptr, display_table_cell, _t(
"table-cell"));
848 case display_table_cell:
849 fix_table_parent(el_ptr, display_table_row, _t(
"table-row"));
852 case display_table_caption:
853 case display_table_column:
854 case display_table_column_group:
862void litehtml::document::fix_table_children(element::ptr& el_ptr, style_display disp,
const tchar_t* disp_str)
865 elements_vector::iterator first_iter = el_ptr->m_children.begin();
866 elements_vector::iterator cur_iter = el_ptr->m_children.begin();
868 auto flush_elements = [&]()
870 element::ptr annon_tag = std::make_shared<html_tag>(shared_from_this());
872 st.add_property(_t(
"display"), disp_str, 0,
false);
873 annon_tag->add_style(st);
874 annon_tag->parent(el_ptr);
875 annon_tag->parse_styles();
876 std::for_each(tmp.begin(), tmp.end(),
877 [&annon_tag](element::ptr& el)
879 annon_tag->appendChild(el);
882 first_iter = el_ptr->m_children.insert(first_iter, annon_tag);
883 cur_iter = first_iter + 1;
884 while (cur_iter != el_ptr->m_children.end() && (*cur_iter)->parent() != el_ptr)
886 cur_iter = el_ptr->m_children.erase(cur_iter);
888 first_iter = cur_iter;
892 while (cur_iter != el_ptr->m_children.end())
894 if ((*cur_iter)->get_display() != disp)
896 if (!(*cur_iter)->is_white_space() || ((*cur_iter)->is_white_space() && !tmp.empty()))
900 first_iter = cur_iter;
902 tmp.push_back((*cur_iter));
906 else if (!tmp.empty())
921void litehtml::document::fix_table_parent(element::ptr& el_ptr, style_display disp,
const tchar_t* disp_str)
923 element::ptr parent = el_ptr->parent();
925 if (parent->get_display() != disp)
927 elements_vector::iterator this_element = std::find_if(parent->m_children.begin(), parent->m_children.end(),
928 [&](element::ptr& el)
937 if (this_element != parent->m_children.end())
939 style_display el_disp = el_ptr->get_display();
940 elements_vector::iterator first = this_element;
941 elements_vector::iterator last = this_element;
942 elements_vector::iterator cur = this_element;
947 if (cur == parent->m_children.begin())
break;
949 if ((*cur)->is_white_space() || (*cur)->get_display() == el_disp)
964 if (cur == parent->m_children.end())
break;
966 if ((*cur)->is_white_space() || (*cur)->get_display() == el_disp)
977 element::ptr annon_tag = std::make_shared<html_tag>(shared_from_this());
979 st.add_property(_t(
"display"), disp_str, 0,
false);
980 annon_tag->add_style(st);
981 annon_tag->parent(parent);
982 annon_tag->parse_styles();
983 std::for_each(first, last + 1,
984 [&annon_tag](element::ptr& el)
986 annon_tag->appendChild(el);
989 first = parent->m_children.erase(first, last + 1);
990 parent->m_children.insert(first, annon_tag);
GumboOutput * gumbo_parse(const char *buffer)
void gumbo_destroy_output(const GumboOptions *options, GumboOutput *output)
const char * gumbo_normalized_tagname(GumboTag tag)
void gumbo_tag_from_original_text(GumboStringPiece *text)
const GumboOptions kGumboDefaultOptions
GumboStringPiece original_tag
union GumboInternalNode::@87 v