#include "html.h"
#include "render_inline_context.h"
#include "document.h"
#include "iterators.h"
int litehtml::render_item_inline_context::_render_content(int /*x*/, int /*y*/, bool /*second_pass*/, const containing_block_context &self_size, formatting_context* fmt_ctx)
{
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& 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::make_unique(el), self_size, fmt_ctx);
}
break;
case iterator_item_type_start_parent:
{
el->clear_inline_boxes();
place_inline(std::make_unique(el), self_size, fmt_ctx);
}
break;
case iterator_item_type_end_parent:
{
place_inline(std::make_unique(el), self_size, fmt_ctx);
}
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)
{
fmt_ctx->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 m_max_line_width;
}
void litehtml::render_item_inline_context::fix_line_width(element_float flt,
const containing_block_context &self_size,
formatting_context* fmt_ctx)
{
if(!m_line_boxes.empty())
{
auto el_front = m_line_boxes.back()->get_first_text_part();
std::vector> 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 > items = std::move(m_line_boxes.back()->items());
m_line_boxes.pop_back();
for(auto& item : items)
{
place_inline(std::move(item), self_size, fmt_ctx);
}
} else
{
int line_top = 0;
line_top = m_line_boxes.back()->top();
int line_left = 0;
int line_right = self_size.render_width;
fmt_ctx->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, fmt_ctx);
}
}
}
}
std::list > litehtml::render_item_inline_context::finish_last_box(bool end_of_render, const containing_block_context &self_size)
{
std::list > 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& el, line_context& line_ctx, const containing_block_context &self_size, formatting_context* fmt_ctx)
{
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 = fmt_ctx->get_cleared_top(el->get_el(), line_top);
line_ctx.left = 0;
line_ctx.right = self_size.render_width;
line_ctx.fix_top();
fmt_ctx->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() || el->get_el()->src_el()->is_block_formatting_context())
{
if (el->get_el()->width() > line_ctx.right - line_ctx.left)
{
line_ctx.top = fmt_ctx->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();
fmt_ctx->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::make_unique(
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 item, const containing_block_context &self_size, formatting_context* fmt_ctx)
{
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, fmt_ctx);
if(ret > m_max_line_width)
{
m_max_line_width = ret;
}
return;
}
line_context line_ctx;
if (!m_line_boxes.empty())
{
line_ctx.top = m_line_boxes.back().get()->top();
}
line_ctx.right = self_size.render_width;
line_ctx.fix_top();
fmt_ctx->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)
{
if(item->get_el()->src_el()->is_inline_box())
{
int min_rendered_width = item->get_el()->render(line_ctx.left, line_ctx.top, self_size.new_width(line_ctx.right), fmt_ctx);
if(min_rendered_width < item->get_el()->width() && item->get_el()->src_el()->css().get_width().is_predefined())
{
item->get_el()->render(line_ctx.left, line_ctx.top, self_size.new_width(min_rendered_width), fmt_ctx);
}
item->set_rendered_min_width(min_rendered_width);
} else if(item->get_el()->src_el()->css().get_display() == 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);
}
}
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, fmt_ctx);
} 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();
fmt_ctx->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())
{
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_first_baseline()
{
int bl;
if(!m_line_boxes.empty())
{
const auto &line = m_line_boxes.front();
bl = line->bottom() - line->baseline() + content_offset_top();
} else
{
bl = height() - margin_bottom();
}
return bl;
}
int litehtml::render_item_inline_context::get_last_baseline()
{
int bl;
if(!m_line_boxes.empty())
{
const auto &line = m_line_boxes.back();
bl = line->bottom() - line->baseline() + content_offset_top();
} else
{
bl = height() - margin_bottom();
}
return bl;
}