mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 08:17:45 +00:00
LibWeb: Don't justify text lines that end in a forced break
These are treated the same as the last line in a block, per CSS-TEXT-3.
This commit is contained in:
parent
358a4fe3cb
commit
79d2c9f3e8
6 changed files with 106 additions and 12 deletions
|
@ -0,0 +1,75 @@
|
||||||
|
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||||
|
BlockContainer <html> at (0,0) content-size 800x118 [BFC] children: not-inline
|
||||||
|
BlockContainer <body> at (8,8) content-size 784x102 children: not-inline
|
||||||
|
BlockContainer <div> at (9,9) content-size 100x100 children: inline
|
||||||
|
line 0 width: 98, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||||
|
frag 0 from TextNode start: 1, length: 3, rect: [9,9 27.15625x17.46875]
|
||||||
|
"foo"
|
||||||
|
frag 1 from TextNode start: 4, length: 1, rect: [36,9 9x17.46875]
|
||||||
|
" "
|
||||||
|
frag 2 from TextNode start: 5, length: 3, rect: [45,9 27.640625x17.46875]
|
||||||
|
"bar"
|
||||||
|
frag 3 from TextNode start: 8, length: 1, rect: [73,9 9x17.46875]
|
||||||
|
" "
|
||||||
|
frag 4 from TextNode start: 9, length: 3, rect: [82,9 27.203125x17.46875]
|
||||||
|
"baz"
|
||||||
|
line 1 width: 98, height: 17.9375, bottom: 35.40625, baseline: 13.53125
|
||||||
|
frag 0 from TextNode start: 13, length: 3, rect: [9,26 27.15625x17.46875]
|
||||||
|
"foo"
|
||||||
|
frag 1 from TextNode start: 16, length: 1, rect: [36,26 8x17.46875]
|
||||||
|
" "
|
||||||
|
frag 2 from TextNode start: 17, length: 3, rect: [44,26 27.640625x17.46875]
|
||||||
|
"bar"
|
||||||
|
frag 3 from TextNode start: 20, length: 1, rect: [72,26 8x17.46875]
|
||||||
|
" "
|
||||||
|
frag 4 from TextNode start: 21, length: 3, rect: [80,26 27.203125x17.46875]
|
||||||
|
"baz"
|
||||||
|
line 2 width: 98, height: 18.40625, bottom: 53.34375, baseline: 13.53125
|
||||||
|
frag 0 from TextNode start: 1, length: 3, rect: [9,43 27.15625x17.46875]
|
||||||
|
"foo"
|
||||||
|
frag 1 from TextNode start: 4, length: 1, rect: [36,43 9x17.46875]
|
||||||
|
" "
|
||||||
|
frag 2 from TextNode start: 5, length: 3, rect: [45,43 27.640625x17.46875]
|
||||||
|
"bar"
|
||||||
|
frag 3 from TextNode start: 8, length: 1, rect: [73,43 9x17.46875]
|
||||||
|
" "
|
||||||
|
frag 4 from TextNode start: 9, length: 3, rect: [82,43 27.203125x17.46875]
|
||||||
|
"baz"
|
||||||
|
line 3 width: 98, height: 17.875, bottom: 70.28125, baseline: 13.53125
|
||||||
|
frag 0 from TextNode start: 13, length: 3, rect: [9,61 27.15625x17.46875]
|
||||||
|
"foo"
|
||||||
|
frag 1 from TextNode start: 16, length: 1, rect: [36,61 9x17.46875]
|
||||||
|
" "
|
||||||
|
frag 2 from TextNode start: 17, length: 3, rect: [45,61 27.640625x17.46875]
|
||||||
|
"bar"
|
||||||
|
frag 3 from TextNode start: 20, length: 1, rect: [73,61 9x17.46875]
|
||||||
|
" "
|
||||||
|
frag 4 from TextNode start: 21, length: 3, rect: [82,61 27.203125x17.46875]
|
||||||
|
"baz"
|
||||||
|
line 4 width: 98, height: 18.34375, bottom: 88.21875, baseline: 13.53125
|
||||||
|
frag 0 from TextNode start: 25, length: 3, rect: [9,78 27.15625x17.46875]
|
||||||
|
"foo"
|
||||||
|
frag 1 from TextNode start: 28, length: 1, rect: [36,78 8x17.46875]
|
||||||
|
" "
|
||||||
|
frag 2 from TextNode start: 29, length: 3, rect: [44,78 27.640625x17.46875]
|
||||||
|
"bar"
|
||||||
|
frag 3 from TextNode start: 32, length: 1, rect: [72,78 8x17.46875]
|
||||||
|
" "
|
||||||
|
frag 4 from TextNode start: 33, length: 3, rect: [80,78 27.203125x17.46875]
|
||||||
|
"baz"
|
||||||
|
line 5 width: 98, height: 17.8125, bottom: 105.15625, baseline: 13.53125
|
||||||
|
frag 0 from TextNode start: 1, length: 3, rect: [9,96 27.15625x17.46875]
|
||||||
|
"foo"
|
||||||
|
frag 1 from TextNode start: 4, length: 1, rect: [36,96 8x17.46875]
|
||||||
|
" "
|
||||||
|
frag 2 from TextNode start: 5, length: 3, rect: [44,96 27.640625x17.46875]
|
||||||
|
"bar"
|
||||||
|
frag 3 from TextNode start: 8, length: 1, rect: [72,96 8x17.46875]
|
||||||
|
" "
|
||||||
|
frag 4 from TextNode start: 9, length: 3, rect: [80,96 27.203125x17.46875]
|
||||||
|
"baz"
|
||||||
|
TextNode <#text>
|
||||||
|
BreakNode <br>
|
||||||
|
TextNode <#text>
|
||||||
|
BreakNode <br>
|
||||||
|
TextNode <#text>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<!doctype html><style>
|
||||||
|
div {
|
||||||
|
text-align: justify;
|
||||||
|
border: 1px solid black;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
</style><div>
|
||||||
|
foo bar baz
|
||||||
|
foo bar baz<br>
|
||||||
|
foo bar baz
|
||||||
|
foo bar baz
|
||||||
|
foo bar baz<br>
|
||||||
|
foo bar baz
|
|
@ -18,8 +18,6 @@
|
||||||
|
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
constexpr double text_justification_threshold = 0.1;
|
|
||||||
|
|
||||||
InlineFormattingContext::InlineFormattingContext(LayoutState& state, BlockContainer const& containing_block, BlockFormattingContext& parent)
|
InlineFormattingContext::InlineFormattingContext(LayoutState& state, BlockContainer const& containing_block, BlockFormattingContext& parent)
|
||||||
: FormattingContext(Type::Inline, state, containing_block, &parent)
|
: FormattingContext(Type::Inline, state, containing_block, &parent)
|
||||||
, m_containing_block_state(state.get(containing_block))
|
, m_containing_block_state(state.get(containing_block))
|
||||||
|
@ -191,13 +189,13 @@ void InlineFormattingContext::apply_justification_to_fragments(CSS::TextJustify
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
CSSPixels excess_horizontal_space = line_box.original_available_width() - line_box.width();
|
// https://www.w3.org/TR/css-text-3/#text-align-property
|
||||||
|
// Unless otherwise specified by text-align-last, the last line before a forced break or the end of the block is start-aligned.
|
||||||
// Only justify the text if the excess horizontal space is less than or
|
// FIXME: Support text-align-last.
|
||||||
// equal to 10%, or if we are not looking at the last line box.
|
if (is_last_line || line_box.m_has_forced_break)
|
||||||
if (is_last_line && excess_horizontal_space / m_available_space->width.to_px().value() > text_justification_threshold)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
CSSPixels excess_horizontal_space = line_box.original_available_width() - line_box.width();
|
||||||
CSSPixels excess_horizontal_space_including_whitespace = excess_horizontal_space;
|
CSSPixels excess_horizontal_space_including_whitespace = excess_horizontal_space;
|
||||||
size_t whitespace_count = 0;
|
size_t whitespace_count = 0;
|
||||||
for (auto& fragment : line_box.fragments()) {
|
for (auto& fragment : line_box.fragments()) {
|
||||||
|
@ -249,7 +247,7 @@ void InlineFormattingContext::generate_line_boxes(LayoutMode layout_mode)
|
||||||
|
|
||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
case InlineLevelIterator::Item::Type::ForcedBreak:
|
case InlineLevelIterator::Item::Type::ForcedBreak:
|
||||||
line_builder.break_line();
|
line_builder.break_line(LineBuilder::ForcedBreak::Yes);
|
||||||
break;
|
break;
|
||||||
case InlineLevelIterator::Item::Type::Element: {
|
case InlineLevelIterator::Item::Type::Element: {
|
||||||
auto& box = verify_cast<Layout::Box>(*item.node);
|
auto& box = verify_cast<Layout::Box>(*item.node);
|
||||||
|
|
|
@ -47,6 +47,7 @@ private:
|
||||||
CSSPixels m_original_available_width { 0 };
|
CSSPixels m_original_available_width { 0 };
|
||||||
|
|
||||||
bool m_has_break { false };
|
bool m_has_break { false };
|
||||||
|
bool m_has_forced_break { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,11 @@ LineBuilder::~LineBuilder()
|
||||||
update_last_line();
|
update_last_line();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LineBuilder::break_line(Optional<CSSPixels> next_item_width)
|
void LineBuilder::break_line(ForcedBreak forced_break, Optional<CSSPixels> next_item_width)
|
||||||
{
|
{
|
||||||
auto last_line_box = ensure_last_line_box();
|
auto& last_line_box = ensure_last_line_box();
|
||||||
last_line_box.m_has_break = true;
|
last_line_box.m_has_break = true;
|
||||||
|
last_line_box.m_has_forced_break = forced_break == ForcedBreak::Yes;
|
||||||
|
|
||||||
update_last_line();
|
update_last_line();
|
||||||
size_t break_count = 0;
|
size_t break_count = 0;
|
||||||
|
|
|
@ -18,7 +18,12 @@ public:
|
||||||
LineBuilder(InlineFormattingContext&, LayoutState&);
|
LineBuilder(InlineFormattingContext&, LayoutState&);
|
||||||
~LineBuilder();
|
~LineBuilder();
|
||||||
|
|
||||||
void break_line(Optional<CSSPixels> next_item_width = {});
|
enum class ForcedBreak {
|
||||||
|
No,
|
||||||
|
Yes,
|
||||||
|
};
|
||||||
|
|
||||||
|
void break_line(ForcedBreak, Optional<CSSPixels> next_item_width = {});
|
||||||
void append_box(Box const&, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin);
|
void append_box(Box const&, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin);
|
||||||
void append_text_chunk(TextNode const&, size_t offset_in_node, size_t length_in_node, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin, CSSPixels content_width, CSSPixels content_height);
|
void append_text_chunk(TextNode const&, size_t offset_in_node, size_t length_in_node, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin, CSSPixels content_width, CSSPixels content_height);
|
||||||
|
|
||||||
|
@ -26,7 +31,7 @@ public:
|
||||||
bool break_if_needed(CSSPixels next_item_width)
|
bool break_if_needed(CSSPixels next_item_width)
|
||||||
{
|
{
|
||||||
if (should_break(next_item_width)) {
|
if (should_break(next_item_width)) {
|
||||||
break_line(next_item_width);
|
break_line(LineBuilder::ForcedBreak::No, next_item_width);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue