summaryrefslogtreecommitdiff
path: root/Libraries
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-06-13 14:59:17 +0200
committerAndreas Kling <kling@serenityos.org>2020-06-13 15:03:16 +0200
commit07ccaa1934395494825606526ed51c1d5c652555 (patch)
tree35be5bce7afe59c27becec78943cb63a480b07a5 /Libraries
parent4ab1b0b43681323e5d0e6b0572781ff70ab74a15 (diff)
downloadserenity-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.cpp42
-rw-r--r--Libraries/LibWeb/Layout/LineBox.cpp7
-rw-r--r--Libraries/LibWeb/Layout/LineBox.h2
-rw-r--r--Libraries/LibWeb/Layout/LineBoxFragment.cpp9
-rw-r--r--Libraries/LibWeb/Layout/LineBoxFragment.h1
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;