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