Cogs.Core
document.cpp
1#ifdef _WIN32
2#pragma warning(disable: 4100) // unreferenced formal parameter
3#pragma warning(disable: 4244) // possible loss of data
4#elif defined(__clang__)
5#pragma clang diagnostic ignored "-Wunused-parameter"
6#pragma clang diagnostic ignored "-Wmissing-field-initializers"
7#endif
8
9
10#include "html.h"
11#include "document.h"
12#include "stylesheet.h"
13#include "html_tag.h"
14#include "el_text.h"
15#include "el_para.h"
16#include "el_space.h"
17#include "el_body.h"
18#include "el_image.h"
19#include "el_table.h"
20#include "el_td.h"
21#include "el_link.h"
22#include "el_title.h"
23#include "el_style.h"
24#include "el_script.h"
25#include "el_comment.h"
26#include "el_cdata.h"
27#include "el_base.h"
28#include "el_anchor.h"
29#include "el_break.h"
30#include "el_div.h"
31#include "el_font.h"
32#include "el_tr.h"
33#include <math.h>
34#include <stdio.h>
35#include <algorithm>
36#include "gumbo.h"
37#include "utf8_strings.h"
38
39litehtml::document::document(litehtml::document_container* objContainer, litehtml::context* ctx)
40{
41 m_container = objContainer;
42 m_context = ctx;
43 m_userdata = nullptr;
44 m_initialised = false;
45}
46
47litehtml::document::~document()
48{
49 m_over_element = 0;
50 if(m_container)
51 {
52 for(fonts_map::iterator f = m_fonts.begin(); f != m_fonts.end(); f++)
53 {
54 m_container->delete_font(f->second.font);
55 }
56 m_container = nullptr;
57 }
58}
59
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();
64 }
65}
66
67litehtml::document::ptr litehtml::document::createFromString( const tchar_t* str, litehtml::document_container* objPainter, litehtml::context* ctx, litehtml::css* user_styles)
68{
69 return createFromUTF8(litehtml_to_utf8(str), objPainter, ctx, user_styles);
70}
71
72litehtml::document::ptr litehtml::document::createFromUTF8(const char* str, litehtml::document_container* objPainter, litehtml::context* ctx, litehtml::css* user_styles)
73{
74 // parse document into GumboOutput
75 GumboOutput* output = gumbo_parse((const char*) str);
76
77 // Create litehtml::document
78 litehtml::document::ptr doc = std::make_shared<litehtml::document>(objPainter, ctx);
79
80 // Create litehtml::elements.
81 elements_vector root_elements;
82 doc->create_node(output->root, root_elements, true, doc);
83 if (!root_elements.empty())
84 {
85 doc->m_root = root_elements.back();
86 }
87 // Destroy GumboOutput
89
90 // Let's process created elements tree
91 if (doc->m_root)
92 {
93 doc->container()->get_media_features(doc->m_media);
94
95 // apply master CSS
96 doc->m_root->apply_stylesheet(ctx->master_css());
97
98 // parse elements attributes
99 doc->m_root->parse_attributes();
100
101 // parse style sheets linked in document
102 media_query_list::ptr media;
103 for (css_text::vector::iterator css = doc->m_css.begin(); css != doc->m_css.end(); css++)
104 {
105 if (!css->media.empty())
106 {
107 media = media_query_list::create_from_string(css->media, doc);
108 }
109 else
110 {
111 media = 0;
112 }
113 doc->m_styles.parse_stylesheet(css->text.c_str(), css->baseurl.c_str(), doc, media);
114 }
115 // Sort css selectors using CSS rules.
116 doc->m_styles.sort_selectors();
117
118 // get current media features
119 if (!doc->m_media_lists.empty())
120 {
121 doc->update_media_lists(doc->m_media);
122 }
123
124 // Apply parsed styles.
125 doc->m_root->apply_stylesheet(doc->m_styles);
126
127 // Apply user styles if any
128 if (user_styles)
129 {
130 doc->m_root->apply_stylesheet(*user_styles);
131 }
132
133 // Parse applied styles in the elements
134 doc->m_root->parse_styles();
135
136 // Now the m_tabular_elements is filled with tabular elements.
137 // We have to check the tabular elements for missing table elements
138 // and create the anonymous boxes in visual table layout
139 doc->fix_tables_layout();
140
141 // Fanaly initialize elements
142 doc->m_root->init();
143 }
144 doc->m_initialised = true;
145
146 return doc;
147}
148
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 )
150{
151 uint_ptr ret = 0;
152
153 if( !name || (name && !t_strcasecmp(name, _t("inherit"))) )
154 {
155 name = m_container->get_default_font_name();
156 }
157
158 if(!size)
159 {
160 size = container()->get_default_font_size();
161 }
162
163 tchar_t strSize[20];
164 t_itoa(size, strSize, 20, 10);
165
166 tstring key = name;
167 key += _t(":");
168 key += strSize;
169 key += _t(":");
170 key += weight;
171 key += _t(":");
172 key += style;
173 key += _t(":");
174 key += decoration;
175
176 if(m_fonts.find(key) == m_fonts.end())
177 {
178 font_style fs = (font_style) value_index(style, font_style_strings, fontStyleNormal);
179 int fw = value_index(weight, font_weight_strings, -1);
180 if(fw >= 0)
181 {
182 switch(fw)
183 {
184 case litehtml::fontWeightBold:
185 fw = 700;
186 break;
187 case litehtml::fontWeightBolder:
188 fw = 600;
189 break;
190 case litehtml::fontWeightLighter:
191 fw = 300;
192 break;
193 default:
194 fw = 400;
195 break;
196 }
197 } else
198 {
199 fw = t_atoi(weight);
200 if(fw < 100)
201 {
202 fw = 400;
203 }
204 }
205
206 unsigned int decor = 0;
207
208 if(decoration)
209 {
210 std::vector<tstring> tokens;
211 split_string(decoration, tokens, _t(" "));
212 for(std::vector<tstring>::iterator i = tokens.begin(); i != tokens.end(); i++)
213 {
214 if(!t_strcasecmp(i->c_str(), _t("underline")))
215 {
216 decor |= font_decoration_underline;
217 } else if(!t_strcasecmp(i->c_str(), _t("line-through")))
218 {
219 decor |= font_decoration_linethrough;
220 } else if(!t_strcasecmp(i->c_str(), _t("overline")))
221 {
222 decor |= font_decoration_overline;
223 }
224 }
225 }
226
227 font_item fi= {0};
228
229 fi.font = m_container->create_font(name, size, fw, fs, decor, &fi.metrics);
230 m_fonts[key] = fi;
231 ret = fi.font;
232 if(fm)
233 {
234 *fm = fi.metrics;
235 }
236 }
237 return ret;
238}
239
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 )
241{
242 if( !name || (name && !t_strcasecmp(name, _t("inherit"))) )
243 {
244 name = m_container->get_default_font_name();
245 }
246
247 if(!size)
248 {
249 size = m_container->get_default_font_size();
250 }
251
252 tchar_t strSize[20];
253 t_itoa(size, strSize, 20, 10);
254
255 tstring key = name;
256 key += _t(":");
257 key += strSize;
258 key += _t(":");
259 key += weight;
260 key += _t(":");
261 key += style;
262 key += _t(":");
263 key += decoration;
264
265 fonts_map::iterator el = m_fonts.find(key);
266
267 if(el != m_fonts.end())
268 {
269 if(fm)
270 {
271 *fm = el->second.metrics;
272 }
273 return el->second.font;
274 }
275 return add_font(name, size, weight, style, decoration, fm);
276}
277
278int litehtml::document::render( int max_width, render_type rt )
279{
280 int ret = 0;
281 if(m_root)
282 {
283 if(rt == render_fixed_only)
284 {
285 m_fixed_boxes.clear();
286 m_root->render_positioned(rt);
287 } else
288 {
289 ret = m_root->render(0, 0, max_width);
290 if(m_root->fetch_positioned())
291 {
292 m_fixed_boxes.clear();
293 m_root->render_positioned(rt);
294 }
295 m_size.width = 0;
296 m_size.height = 0;
297 m_root->calc_document_size(m_size);
298 }
299 }
300 return ret;
301}
302
303void litehtml::document::draw( uint_ptr hdc, int x, int y, const position* clip )
304{
305 if(m_root)
306 {
307 m_root->draw(hdc, x, y, clip);
308 m_root->draw_stacking_context(hdc, x, y, clip, true);
309 }
310}
311
312int litehtml::document::cvt_units( const tchar_t* str, int fontSize, bool* is_percent/*= 0*/ ) const
313{
314 if(!str) return 0;
315
316 css_length val;
317 val.fromString(str);
318 if(is_percent && val.units() == css_units_percentage && !val.is_predefined())
319 {
320 *is_percent = true;
321 }
322 return cvt_units(val, fontSize);
323}
324
325int litehtml::document::cvt_units( css_length& val, int fontSize, int size ) const
326{
327 if(val.is_predefined())
328 {
329 return 0;
330 }
331 int ret = 0;
332 switch(val.units())
333 {
334 case css_units_percentage:
335 ret = val.calc_percent(size);
336 break;
337 case css_units_em:
338 ret = round_f(val.val() * fontSize);
339 val.set_value((float) ret, css_units_px);
340 break;
341 case css_units_pt:
342 ret = m_container->pt_to_px((int) val.val());
343 val.set_value((float) ret, css_units_px);
344 break;
345 case css_units_in:
346 ret = m_container->pt_to_px((int) (val.val() * 72));
347 val.set_value((float) ret, css_units_px);
348 break;
349 case css_units_cm:
350 ret = m_container->pt_to_px((int) (val.val() * 0.3937 * 72));
351 val.set_value((float) ret, css_units_px);
352 break;
353 case css_units_mm:
354 ret = m_container->pt_to_px((int) (val.val() * 0.3937 * 72) / 10);
355 val.set_value((float) ret, css_units_px);
356 break;
357 case css_units_vw:
358 ret = (int)((double)m_media.width * (double)val.val() / 100.0);
359 break;
360 case css_units_vh:
361 ret = (int)((double)m_media.height * (double)val.val() / 100.0);
362 break;
363 case css_units_vmin:
364 ret = (int)((double)std::min(m_media.height, m_media.width) * (double)val.val() / 100.0);
365 break;
366 case css_units_vmax:
367 ret = (int)((double)std::max(m_media.height, m_media.width) * (double)val.val() / 100.0);
368 break;
369 case css_units_rem:
370 ret = (int) ((double) m_root->get_font_size() * (double) val.val());
371 val.set_value((float) ret, css_units_px);
372 break;
373 default:
374 ret = (int) val.val();
375 break;
376 }
377 return ret;
378}
379
380int litehtml::document::width() const
381{
382 return m_size.width;
383}
384
385int litehtml::document::height() const
386{
387 return m_size.height;
388}
389
390void litehtml::document::add_stylesheet( const tchar_t* str, const tchar_t* baseurl, const tchar_t* media )
391{
392 if(str && str[0])
393 {
394 m_css.push_back(css_text(str, baseurl, media));
395 }
396}
397
398bool litehtml::document::on_mouse_move(int x, int y)
399{
400 if (!m_root)
401 {
402 return false;
403 }
404
405 if (m_over_element)
406 {
407 m_container->on_mouse_move(m_over_element, x, y);
408
409 return true;
410 }
411
412 return false;
413}
414
415bool litehtml::document::on_mouse_over( int x, int y, int client_x, int client_y, position::vector& redraw_boxes )
416{
417 if(!m_root)
418 {
419 return false;
420 }
421
422 element::ptr over_el = m_root->get_element_by_point(x, y, client_x, client_y);
423
424 bool state_was_changed = false;
425
426 if(over_el != m_over_element)
427 {
428 if(m_over_element)
429 {
430 if(m_over_element->on_mouse_leave())
431 {
432 state_was_changed = true;
433 m_container->on_mouse_leave(m_over_element);
434 }
435 }
436 m_over_element = over_el;
437 }
438
439 const tchar_t* cursor = 0;
440
441 if(m_over_element)
442 {
443 if(m_over_element->on_mouse_over())
444 {
445 state_was_changed = true;
446 m_container->on_mouse_enter(m_over_element);
447 }
448 cursor = m_over_element->get_cursor();
449 }
450
451 m_container->set_cursor(cursor ? cursor : _t("auto"));
452
453 if(state_was_changed)
454 {
455 return m_root->find_styles_changes(redraw_boxes, 0, 0);
456 }
457 return false;
458}
459
460bool litehtml::document::on_mouse_leave( position::vector& redraw_boxes )
461{
462 if(!m_root)
463 {
464 return false;
465 }
466 if(m_over_element)
467 {
468 if(m_over_element->on_mouse_leave())
469 {
470 m_container->on_mouse_leave(m_over_element);
471 return m_root->find_styles_changes(redraw_boxes, 0, 0);
472 }
473 }
474 return false;
475}
476
477bool litehtml::document::on_lbutton_down( int x, int y, int client_x, int client_y, position::vector& redraw_boxes )
478{
479 if(!m_root)
480 {
481 return false;
482 }
483
484 element::ptr over_el = m_root->get_element_by_point(x, y, client_x, client_y);
485
486 bool state_was_changed = false;
487
488 if(over_el != m_over_element)
489 {
490 if(m_over_element)
491 {
492 if(m_over_element->on_mouse_leave())
493 {
494 state_was_changed = true;
495 m_container->on_mouse_leave(m_over_element);
496 }
497 }
498 m_over_element = over_el;
499 if(m_over_element)
500 {
501 if(m_over_element->on_mouse_over())
502 {
503 state_was_changed = true;
504 m_container->on_mouse_enter(m_over_element);
505 }
506 }
507 }
508
509 const tchar_t* cursor = 0;
510
511 if(m_over_element)
512 {
513 if(m_over_element->on_lbutton_down())
514 {
515 state_was_changed = true;
516 }
517 cursor = m_over_element->get_cursor();
518 }
519
520 m_container->set_cursor(cursor ? cursor : _t("auto"));
521
522 if(state_was_changed)
523 {
524 return m_root->find_styles_changes(redraw_boxes, 0, 0);
525 }
526
527 return false;
528}
529
530bool litehtml::document::on_lbutton_up( int x, int y, int client_x, int client_y, position::vector& redraw_boxes )
531{
532 if(!m_root)
533 {
534 return false;
535 }
536 if(m_over_element)
537 {
538 if(m_over_element->on_lbutton_up())
539 {
540 return m_root->find_styles_changes(redraw_boxes, 0, 0);
541 }
542 }
543 return false;
544}
545
546litehtml::element::ptr litehtml::document::create_element(const tchar_t* tag_name, const string_map& attributes)
547{
548 element::ptr newTag;
549 document::ptr this_doc = shared_from_this();
550 if(m_container)
551 {
552 newTag = m_container->create_element(tag_name, attributes, this_doc);
553 }
554 if(!newTag)
555 {
556 if(!t_strcmp(tag_name, _t("br")))
557 {
558 newTag = std::make_shared<litehtml::el_break>(this_doc);
559 } else if(!t_strcmp(tag_name, _t("p")))
560 {
561 newTag = std::make_shared<litehtml::el_para>(this_doc);
562 } else if(!t_strcmp(tag_name, _t("img")))
563 {
564 newTag = std::make_shared<litehtml::el_image>(this_doc);
565 } else if(!t_strcmp(tag_name, _t("table")))
566 {
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")))
569 {
570 newTag = std::make_shared<litehtml::el_td>(this_doc);
571 } else if(!t_strcmp(tag_name, _t("link")))
572 {
573 newTag = std::make_shared<litehtml::el_link>(this_doc);
574 } else if(!t_strcmp(tag_name, _t("title")))
575 {
576 newTag = std::make_shared<litehtml::el_title>(this_doc);
577 } else if(!t_strcmp(tag_name, _t("a")))
578 {
579 newTag = std::make_shared<litehtml::el_anchor>(this_doc);
580 } else if(!t_strcmp(tag_name, _t("tr")))
581 {
582 newTag = std::make_shared<litehtml::el_tr>(this_doc);
583 } else if(!t_strcmp(tag_name, _t("style")))
584 {
585 newTag = std::make_shared<litehtml::el_style>(this_doc);
586 } else if(!t_strcmp(tag_name, _t("base")))
587 {
588 newTag = std::make_shared<litehtml::el_base>(this_doc);
589 } else if(!t_strcmp(tag_name, _t("body")))
590 {
591 newTag = std::make_shared<litehtml::el_body>(this_doc);
592 } else if(!t_strcmp(tag_name, _t("div")))
593 {
594 newTag = std::make_shared<litehtml::el_div>(this_doc);
595 } else if(!t_strcmp(tag_name, _t("script")))
596 {
597 newTag = std::make_shared<litehtml::el_script>(this_doc);
598 } else if(!t_strcmp(tag_name, _t("font")))
599 {
600 newTag = std::make_shared<litehtml::el_font>(this_doc);
601 } else
602 {
603 newTag = std::make_shared<litehtml::html_tag>(this_doc);
604 }
605 }
606
607 if(newTag)
608 {
609 newTag->set_tagName(tag_name);
610 for (string_map::const_iterator iter = attributes.begin(); iter != attributes.end(); iter++)
611 {
612 newTag->set_attr(iter->first.c_str(), iter->second.c_str());
613 }
614 }
615
616 return newTag;
617}
618
619void litehtml::document::get_fixed_boxes( position::vector& fixed_boxes )
620{
621 fixed_boxes = m_fixed_boxes;
622}
623
624void litehtml::document::add_fixed_box( const position& pos )
625{
626 m_fixed_boxes.push_back(pos);
627}
628
629bool litehtml::document::media_changed()
630{
631 if(!m_media_lists.empty())
632 {
633 container()->get_media_features(m_media);
634 if (update_media_lists(m_media))
635 {
636 m_root->refresh_styles();
637 m_root->parse_styles();
638 return true;
639 }
640 }
641 return false;
642}
643
644bool litehtml::document::lang_changed()
645{
646 if(!m_media_lists.empty())
647 {
648 tstring culture;
649 container()->get_language(m_lang, culture);
650 if(!culture.empty())
651 {
652 m_culture = m_lang + _t('-') + culture;
653 }
654 else
655 {
656 m_culture.clear();
657 }
658 m_root->refresh_styles();
659 m_root->parse_styles();
660 return true;
661 }
662 return false;
663}
664
665bool litehtml::document::update_media_lists(const media_features& features)
666{
667 bool update_styles = false;
668 for(media_query_list::vector::iterator iter = m_media_lists.begin(); iter != m_media_lists.end(); iter++)
669 {
670 if((*iter)->apply_media_features(features))
671 {
672 update_styles = true;
673 }
674 }
675 return update_styles;
676}
677
678void litehtml::document::add_media_list( media_query_list::ptr list )
679{
680 if(list)
681 {
682 if(std::find(m_media_lists.begin(), m_media_lists.end(), list) == m_media_lists.end())
683 {
684 m_media_lists.push_back(list);
685 }
686 }
687}
688
689void litehtml::document::create_node(void* gnode, elements_vector& elements, bool parseTextNode, document::ptr doc)
690{
691 GumboNode* node = (GumboNode*)gnode;
692 switch (node->type)
693 {
695 {
696 string_map attrs;
697 GumboAttribute* attr;
698 for (unsigned int i = 0; i < node->v.element.attributes.length; i++)
699 {
700 attr = (GumboAttribute*)node->v.element.attributes.data[i];
701 attrs[tstring(litehtml_from_utf8(attr->name))] = litehtml_from_utf8(attr->value);
702 }
703
704
705 element::ptr ret;
706 const char* tag = gumbo_normalized_tagname(node->v.element.tag);
707 if (tag[0])
708 {
709 ret = doc->create_element(litehtml_from_utf8(tag), attrs);
710 }
711 else
712 {
713 if (node->v.element.original_tag.data && node->v.element.original_tag.length)
714 {
715 std::string strA;
717 strA.append(node->v.element.original_tag.data, node->v.element.original_tag.length);
718 ret = doc->create_element(litehtml_from_utf8(strA.c_str()), attrs);
719 }
720 }
721 if (!strcmp(tag, "script"))
722 {
723 parseTextNode = false;
724 }
725 if (ret)
726 {
727 elements_vector child;
728 for (unsigned int i = 0; i < node->v.element.children.length; i++)
729 {
730 child.clear();
731 create_node(static_cast<GumboNode*> (node->v.element.children.data[i]), child, parseTextNode, doc);
732 std::for_each(child.begin(), child.end(),
733 [&ret](element::ptr& el)
734 {
735 ret->appendChild(el);
736 }
737 );
738 }
739 elements.push_back(ret);
740 }
741 }
742 break;
743 case GUMBO_NODE_TEXT:
744 {
745 std::wstring str;
746 std::wstring str_in = (const wchar_t*) (utf8_to_wchar(node->v.text.text));
747 if (!parseTextNode)
748 {
749 elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str_in.c_str()), doc));
750 break;
751 }
752 ucode_t c;
753 for (size_t i = 0; i < str_in.length(); i++)
754 {
755 c = (ucode_t) str_in[i];
756 if (c <= ' ' && (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f'))
757 {
758 if (!str.empty())
759 {
760 elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), doc));
761 str.clear();
762 }
763 str += c;
764 elements.push_back(std::make_shared<el_space>(litehtml_from_wchar(str.c_str()), doc));
765 str.clear();
766 }
767 // CJK character range
768 else if (c >= 0x4E00 && c <= 0x9FCC)
769 {
770 if (!str.empty())
771 {
772 elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), doc));
773 str.clear();
774 }
775 str += c;
776 elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), doc));
777 str.clear();
778 }
779 else
780 {
781 str += c;
782 }
783 }
784 if (!str.empty())
785 {
786 elements.push_back(std::make_shared<el_text>(litehtml_from_wchar(str.c_str()), doc));
787 }
788 }
789 break;
790 case GUMBO_NODE_CDATA:
791 {
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);
795 }
796 break;
798 {
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);
802 }
803 break;
805 {
806 tstring str = litehtml_from_utf8(node->v.text.text);
807 for (size_t i = 0; i < str.length(); i++)
808 {
809 elements.push_back(std::make_shared<el_space>(str.substr(i, 1).c_str(), doc));
810 }
811 }
812 break;
813 default:
814 break;
815 }
816}
817
818void litehtml::document::fix_tables_layout()
819{
820 size_t i = 0;
821 while (i < m_tabular_elements.size())
822 {
823 element::ptr el_ptr = m_tabular_elements[i];
824
825 switch (el_ptr->get_display())
826 {
827 case display_inline_table:
828 case display_table:
829 fix_table_children(el_ptr, display_table_row_group, _t("table-row-group"));
830 break;
831 case display_table_footer_group:
832 case display_table_row_group:
833 case display_table_header_group:
834 {
835 element::ptr parent = el_ptr->parent();
836 if (parent)
837 {
838 if (parent->get_display() != display_inline_table)
839 fix_table_parent(el_ptr, display_table, _t("table"));
840 }
841 fix_table_children(el_ptr, display_table_row, _t("table-row"));
842 }
843 break;
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"));
847 break;
848 case display_table_cell:
849 fix_table_parent(el_ptr, display_table_row, _t("table-row"));
850 break;
851 // TODO: make table layout fix for table-caption, table-column etc. elements
852 case display_table_caption:
853 case display_table_column:
854 case display_table_column_group:
855 default:
856 break;
857 }
858 i++;
859 }
860}
861
862void litehtml::document::fix_table_children(element::ptr& el_ptr, style_display disp, const tchar_t* disp_str)
863{
864 elements_vector tmp;
865 elements_vector::iterator first_iter = el_ptr->m_children.begin();
866 elements_vector::iterator cur_iter = el_ptr->m_children.begin();
867
868 auto flush_elements = [&]()
869 {
870 element::ptr annon_tag = std::make_shared<html_tag>(shared_from_this());
871 style st;
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)
878 {
879 annon_tag->appendChild(el);
880 }
881 );
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)
885 {
886 cur_iter = el_ptr->m_children.erase(cur_iter);
887 }
888 first_iter = cur_iter;
889 tmp.clear();
890 };
891
892 while (cur_iter != el_ptr->m_children.end())
893 {
894 if ((*cur_iter)->get_display() != disp)
895 {
896 if (!(*cur_iter)->is_white_space() || ((*cur_iter)->is_white_space() && !tmp.empty()))
897 {
898 if (tmp.empty())
899 {
900 first_iter = cur_iter;
901 }
902 tmp.push_back((*cur_iter));
903 }
904 cur_iter++;
905 }
906 else if (!tmp.empty())
907 {
908 flush_elements();
909 }
910 else
911 {
912 cur_iter++;
913 }
914 }
915 if (!tmp.empty())
916 {
917 flush_elements();
918 }
919}
920
921void litehtml::document::fix_table_parent(element::ptr& el_ptr, style_display disp, const tchar_t* disp_str)
922{
923 element::ptr parent = el_ptr->parent();
924
925 if (parent->get_display() != disp)
926 {
927 elements_vector::iterator this_element = std::find_if(parent->m_children.begin(), parent->m_children.end(),
928 [&](element::ptr& el)
929 {
930 if (el == el_ptr)
931 {
932 return true;
933 }
934 return false;
935 }
936 );
937 if (this_element != parent->m_children.end())
938 {
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;
943
944 // find first element with same display
945 while (true)
946 {
947 if (cur == parent->m_children.begin()) break;
948 cur--;
949 if ((*cur)->is_white_space() || (*cur)->get_display() == el_disp)
950 {
951 first = cur;
952 }
953 else
954 {
955 break;
956 }
957 }
958
959 // find last element with same display
960 cur = this_element;
961 while (true)
962 {
963 cur++;
964 if (cur == parent->m_children.end()) break;
965
966 if ((*cur)->is_white_space() || (*cur)->get_display() == el_disp)
967 {
968 last = cur;
969 }
970 else
971 {
972 break;
973 }
974 }
975
976 // extract elements with the same display and wrap them with anonymous object
977 element::ptr annon_tag = std::make_shared<html_tag>(shared_from_this());
978 style st;
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)
985 {
986 annon_tag->appendChild(el);
987 }
988 );
989 first = parent->m_children.erase(first, last + 1);
990 parent->m_children.insert(first, annon_tag);
991 }
992 }
993}
GumboOutput * gumbo_parse(const char *buffer)
@ GUMBO_NODE_CDATA
Definition: gumbo.h:301
@ GUMBO_NODE_TEXT
Definition: gumbo.h:299
@ GUMBO_NODE_WHITESPACE
Definition: gumbo.h:305
@ GUMBO_NODE_COMMENT
Definition: gumbo.h:303
@ GUMBO_NODE_ELEMENT
Definition: gumbo.h:297
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
const char * name
Definition: gumbo.h:239
const char * value
Definition: gumbo.h:253
GumboVector attributes
Definition: gumbo.h:512
GumboTag tag
Definition: gumbo.h:482
GumboVector children
Definition: gumbo.h:479
GumboStringPiece original_tag
Definition: gumbo.h:493
union GumboInternalNode::@87 v
GumboNodeType type
Definition: gumbo.h:521
const char * data
Definition: gumbo.h:92
size_t length
Definition: gumbo.h:95
const char * text
Definition: gumbo.h:455
void ** data
Definition: gumbo.h:128
unsigned int length
Definition: gumbo.h:131