diff options
author | Andreas Kling <kling@serenityos.org> | 2020-06-13 14:59:17 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-06-13 15:03:16 +0200 |
commit | 07ccaa1934395494825606526ed51c1d5c652555 (patch) | |
tree | 35be5bce7afe59c27becec78943cb63a480b07a5 /Libraries | |
parent | 4ab1b0b43681323e5d0e6b0572781ff70ab74a15 (diff) | |
download | serenity-07ccaa1934395494825606526ed51c1d5c652555.zip |
LibWeb: Teach line layout to collapse whitespace across fragments
This kind of HTML now produces a single piece of whitespace:
<span> </span> <span> </span> <span> </span>
We achieve this by checking if the last fragment on the last line ends
in whitespace. If so, we either don't add a fragment at all (for the
current chunk) or we simply skip over all whitespace at the head of
the current chunk (instead of collapsing it to a single ' '.)
Diffstat (limited to 'Libraries')
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutText.cpp | 42 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LineBox.cpp | 7 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LineBox.h | 2 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LineBoxFragment.cpp | 9 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LineBoxFragment.h | 1 |
5 files changed, 46 insertions, 15 deletions
diff --git a/Libraries/LibWeb/Layout/LayoutText.cpp b/Libraries/LibWeb/Layout/LayoutText.cpp index 196ffb99e4..c8f3558e8b 100644 --- a/Libraries/LibWeb/Layout/LayoutText.cpp +++ b/Libraries/LibWeb/Layout/LayoutText.cpp @@ -46,7 +46,7 @@ LayoutText::~LayoutText() { } -static bool is_all_whitespace(const String& string) +static bool is_all_whitespace(const StringView& string) { for (size_t i = 0; i < string.length(); ++i) { if (!isspace(string[i])) @@ -111,7 +111,8 @@ void LayoutText::for_each_chunk(Callback callback, LayoutMode layout_mode, bool int length = view.byte_offset_of(it) - view.byte_offset_of(start_of_chunk); if (has_breaking_newline || length > 0) { - callback(view.substring_view(start, length), start, length, has_breaking_newline); + auto chunk_view = view.substring_view(start, length); + callback(chunk_view, start, length, has_breaking_newline, is_all_whitespace(chunk_view.as_string())); } start_of_chunk = it; @@ -160,17 +161,23 @@ void LayoutText::split_into_lines_by_rules(LayoutBlock& container, LayoutMode la if (do_collapse) { auto utf8_view = Utf8View(node().data()); StringBuilder builder(node().data().length()); - for (auto it = utf8_view.begin(); it != utf8_view.end(); ++it) { + auto it = utf8_view.begin(); + auto skip_over_whitespace = [&] { + auto prev = it; + while (it != utf8_view.end() && isspace(*it)) { + prev = it; + ++it; + } + it = prev; + }; + if (line_boxes.last().ends_in_whitespace()) + skip_over_whitespace(); + for (; it != utf8_view.end(); ++it) { if (!isspace(*it)) { builder.append(utf8_view.as_string().characters_without_null_termination() + utf8_view.byte_offset_of(it), it.codepoint_length_in_bytes()); } else { builder.append(' '); - auto prev = it; - while (it != utf8_view.end() && isspace(*it)) { - prev = it; - ++it; - } - it = prev; + skip_over_whitespace(); } } m_text_for_rendering = builder.to_string(); @@ -182,25 +189,30 @@ void LayoutText::split_into_lines_by_rules(LayoutBlock& container, LayoutMode la // !do_wrap_lines => chunks_are_lines struct Chunk { Utf8View view; - int start; - int length; - bool is_break; + int start { 0 }; + int length { 0 }; + bool is_break { false }; + bool is_all_whitespace { false }; }; Vector<Chunk> chunks; for_each_chunk( - [&](const Utf8View& view, int start, int length, bool is_break) { - chunks.append({ Utf8View(view), start, length, is_break }); + [&](const Utf8View& view, int start, int length, bool is_break, bool is_all_whitespace) { + chunks.append({ Utf8View(view), start, length, is_break, is_all_whitespace }); }, layout_mode, do_wrap_lines, do_wrap_breaks); for (size_t i = 0; i < chunks.size(); ++i) { auto& chunk = chunks[i]; + // Collapse entire fragment into non-existence if previous fragment on line ended in whitespace. + if (do_collapse && line_boxes.last().ends_in_whitespace() && chunk.is_all_whitespace) + continue; + float chunk_width; bool need_collapse = false; if (do_wrap_lines) { - bool need_collapse = do_collapse && isspace(*chunk.view.begin()); + need_collapse = do_collapse && isspace(*chunk.view.begin()) && line_boxes.last().ends_in_whitespace(); if (need_collapse) chunk_width = space_width; diff --git a/Libraries/LibWeb/Layout/LineBox.cpp b/Libraries/LibWeb/Layout/LineBox.cpp index e9842e391f..b4ae0e08f8 100644 --- a/Libraries/LibWeb/Layout/LineBox.cpp +++ b/Libraries/LibWeb/Layout/LineBox.cpp @@ -73,4 +73,11 @@ void LineBox::trim_trailing_whitespace() } } +bool LineBox::ends_in_whitespace() const +{ + if (m_fragments.is_empty()) + return false; + return m_fragments.last().ends_in_whitespace(); +} + } diff --git a/Libraries/LibWeb/Layout/LineBox.h b/Libraries/LibWeb/Layout/LineBox.h index 67333325ae..cb28084e75 100644 --- a/Libraries/LibWeb/Layout/LineBox.h +++ b/Libraries/LibWeb/Layout/LineBox.h @@ -45,6 +45,8 @@ public: void trim_trailing_whitespace(); + bool ends_in_whitespace() const; + private: friend class LayoutBlock; NonnullOwnPtrVector<LineBoxFragment> m_fragments; diff --git a/Libraries/LibWeb/Layout/LineBoxFragment.cpp b/Libraries/LibWeb/Layout/LineBoxFragment.cpp index 858c0ebe68..40b19a59e0 100644 --- a/Libraries/LibWeb/Layout/LineBoxFragment.cpp +++ b/Libraries/LibWeb/Layout/LineBoxFragment.cpp @@ -30,6 +30,7 @@ #include <LibWeb/Layout/LayoutText.h> #include <LibWeb/Layout/LineBoxFragment.h> #include <LibWeb/RenderingContext.h> +#include <ctype.h> namespace Web { @@ -45,6 +46,14 @@ void LineBoxFragment::render(RenderingContext& context) } } +bool LineBoxFragment::ends_in_whitespace() const +{ + auto text = this->text(); + if (text.is_empty()) + return false; + return isspace(text[text.length() - 1]); +} + bool LineBoxFragment::is_justifiable_whitespace() const { return text() == " "; diff --git a/Libraries/LibWeb/Layout/LineBoxFragment.h b/Libraries/LibWeb/Layout/LineBoxFragment.h index ba64d39468..3dd0967a47 100644 --- a/Libraries/LibWeb/Layout/LineBoxFragment.h +++ b/Libraries/LibWeb/Layout/LineBoxFragment.h @@ -62,6 +62,7 @@ public: void render(RenderingContext&); + bool ends_in_whitespace() const; bool is_justifiable_whitespace() const; StringView text() const; |