mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 00:57:45 +00:00
LibWeb: Refactor text justification code + only justify below threshold
All the justification-related code is now in InlineFormattingContext::apply_justification_to_fragments and is performed after all the line boxes have been added. Text justification now only happens on the last line if the excess space including whitespace is below a certain threshold. 10% seemed reasonable since it prevents the "over-justification" of text. Note that fragments in line boxes before the last one are always justified.
This commit is contained in:
parent
f0a1ab6f84
commit
7fe3f2d970
3 changed files with 53 additions and 26 deletions
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
|
constexpr float text_justification_threshold = 0.1;
|
||||||
|
|
||||||
InlineFormattingContext::InlineFormattingContext(FormattingState& state, BlockContainer const& containing_block, BlockFormattingContext& parent)
|
InlineFormattingContext::InlineFormattingContext(FormattingState& state, BlockContainer const& containing_block, BlockFormattingContext& parent)
|
||||||
: FormattingContext(Type::Inline, state, containing_block, &parent)
|
: FormattingContext(Type::Inline, state, containing_block, &parent)
|
||||||
{
|
{
|
||||||
|
@ -164,6 +166,45 @@ void InlineFormattingContext::dimension_box_on_line(Box const& box, LayoutMode l
|
||||||
dump_tree(box);
|
dump_tree(box);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InlineFormattingContext::apply_justification_to_fragments(FormattingState::NodeState const& containing_block_state, LineBox& line_box, bool is_last_line)
|
||||||
|
{
|
||||||
|
float excess_horizontal_space = containing_block_state.content_width - line_box.width();
|
||||||
|
|
||||||
|
// Only justify the text if the excess horizontal space is less than or
|
||||||
|
// equal to 10%, or if we are not looking at the last line box.
|
||||||
|
if (is_last_line && excess_horizontal_space / containing_block_state.content_width > text_justification_threshold)
|
||||||
|
return;
|
||||||
|
|
||||||
|
float excess_horizontal_space_including_whitespace = excess_horizontal_space;
|
||||||
|
size_t whitespace_count = 0;
|
||||||
|
for (auto& fragment : line_box.fragments()) {
|
||||||
|
if (fragment.is_justifiable_whitespace()) {
|
||||||
|
++whitespace_count;
|
||||||
|
excess_horizontal_space_including_whitespace += fragment.width();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float justified_space_width = whitespace_count > 0 ? (excess_horizontal_space_including_whitespace / static_cast<float>(whitespace_count)) : 0;
|
||||||
|
|
||||||
|
// This is the amount that each fragment will be offset by. If a whitespace
|
||||||
|
// fragment is shorter than the justified space width, it increases to push
|
||||||
|
// subsequent fragments, and decreases to pull them back otherwise.
|
||||||
|
float running_diff = 0;
|
||||||
|
for (size_t i = 0; i < line_box.fragments().size(); ++i) {
|
||||||
|
auto& fragment = line_box.fragments()[i];
|
||||||
|
|
||||||
|
auto offset = fragment.offset();
|
||||||
|
offset.translate_by(running_diff, 0);
|
||||||
|
fragment.set_offset(offset);
|
||||||
|
|
||||||
|
if (fragment.is_justifiable_whitespace()
|
||||||
|
&& fragment.width() != justified_space_width) {
|
||||||
|
running_diff += justified_space_width - fragment.width();
|
||||||
|
fragment.set_width(justified_space_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void InlineFormattingContext::generate_line_boxes(LayoutMode layout_mode)
|
void InlineFormattingContext::generate_line_boxes(LayoutMode layout_mode)
|
||||||
{
|
{
|
||||||
auto& containing_block_state = m_state.get_mutable(containing_block());
|
auto& containing_block_state = m_state.get_mutable(containing_block());
|
||||||
|
@ -221,6 +262,17 @@ void InlineFormattingContext::generate_line_boxes(LayoutMode layout_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
line_builder.remove_last_line_if_empty();
|
line_builder.remove_last_line_if_empty();
|
||||||
|
|
||||||
|
auto const& containing_block = this->containing_block();
|
||||||
|
auto text_align = containing_block.computed_values().text_align();
|
||||||
|
if (text_align == CSS::TextAlign::Justify) {
|
||||||
|
auto const& containing_block_state = m_state.get(containing_block);
|
||||||
|
for (size_t i = 0; i < line_boxes.size(); i++) {
|
||||||
|
auto& line_box = line_boxes[i];
|
||||||
|
auto is_last_line = i == line_boxes.size() - 1;
|
||||||
|
apply_justification_to_fragments(containing_block_state, line_box, is_last_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void generate_line_boxes(LayoutMode);
|
void generate_line_boxes(LayoutMode);
|
||||||
|
void apply_justification_to_fragments(FormattingState::NodeState const& containing_block_state, LineBox&, bool is_last_line);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,19 +125,6 @@ void LineBuilder::update_last_line()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
float excess_horizontal_space_including_whitespace = excess_horizontal_space;
|
|
||||||
size_t whitespace_count = 0;
|
|
||||||
if (text_align == CSS::TextAlign::Justify) {
|
|
||||||
for (auto& fragment : line_box.fragments()) {
|
|
||||||
if (fragment.is_justifiable_whitespace()) {
|
|
||||||
++whitespace_count;
|
|
||||||
excess_horizontal_space_including_whitespace += fragment.width();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float justified_space_width = whitespace_count > 0 ? (excess_horizontal_space_including_whitespace / static_cast<float>(whitespace_count)) : 0;
|
|
||||||
|
|
||||||
auto fragment_baseline = [&](auto const& fragment) -> float {
|
auto fragment_baseline = [&](auto const& fragment) -> float {
|
||||||
if (fragment.layout_node().is_text_node())
|
if (fragment.layout_node().is_text_node())
|
||||||
return fragment.layout_node().font().baseline();
|
return fragment.layout_node().font().baseline();
|
||||||
|
@ -193,19 +180,6 @@ void LineBuilder::update_last_line()
|
||||||
fragment.set_offset({ new_fragment_x, new_fragment_y });
|
fragment.set_offset({ new_fragment_x, new_fragment_y });
|
||||||
|
|
||||||
bottom = max(bottom, new_fragment_y + fragment.height() + fragment.border_box_bottom());
|
bottom = max(bottom, new_fragment_y + fragment.height() + fragment.border_box_bottom());
|
||||||
|
|
||||||
if (text_align == CSS::TextAlign::Justify
|
|
||||||
&& fragment.is_justifiable_whitespace()
|
|
||||||
&& fragment.width() != justified_space_width) {
|
|
||||||
float diff = justified_space_width - fragment.width();
|
|
||||||
fragment.set_width(justified_space_width);
|
|
||||||
// Shift subsequent sibling fragments to the right to adjust for change in width.
|
|
||||||
for (size_t j = i + 1; j < line_box.fragments().size(); ++j) {
|
|
||||||
auto offset = line_box.fragments()[j].offset();
|
|
||||||
offset.translate_by(diff, 0);
|
|
||||||
line_box.fragments()[j].set_offset(offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
line_box.m_bottom = bottom;
|
line_box.m_bottom = bottom;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue