diff --git a/engine/src/flutter/third_party/txt/src/txt/paragraph.cc b/engine/src/flutter/third_party/txt/src/txt/paragraph.cc index 4c6a2eacfb..40b0c300c5 100644 --- a/engine/src/flutter/third_party/txt/src/txt/paragraph.cc +++ b/engine/src/flutter/third_party/txt/src/txt/paragraph.cc @@ -479,10 +479,6 @@ void Paragraph::Layout(double width, bool force) { } } - // TODO(garyq): Make GetRectsForRange return a zero-width box for the - // excluded whitespace such that the caret placement is correct regardless - // of the index being whitespace or not. - // Exclude trailing whitespace from right and center-justified lines so the // last visible character in the line will be flush with the right margin. size_t line_end_index = @@ -740,6 +736,23 @@ void Paragraph::Layout(double width, bool force) { code_unit_runs_.insert(code_unit_runs_.end(), line_code_unit_runs.begin(), line_code_unit_runs.end()); + // Add extra empty metrics for skipped whitespace at line end. This allows + // GetRectsForRange to properly draw empty rects at the ends of lines with + // truncated whitespace. + if (line_end_index < line_range.end) { + std::vector empty_glyph_positions; + double end_x = line_code_unit_runs.back().positions.back().x_pos.end; + for (size_t index = line_end_index; index < line_range.end; ++index) { + empty_glyph_positions.emplace_back(end_x, 0, index, 1); + } + code_unit_runs_.emplace_back( + std::move(empty_glyph_positions), + Range(line_end_index, line_range.end), + Range(end_x, end_x), line_code_unit_runs.back().line_number, + line_code_unit_runs.back().font_metrics, + line_code_unit_runs.back().direction); + } + double max_line_spacing = 0; double max_descent = 0; SkScalar max_unscaled_ascent = 0; diff --git a/engine/src/flutter/third_party/txt/tests/paragraph_unittests.cc b/engine/src/flutter/third_party/txt/tests/paragraph_unittests.cc index b7589f7e21..2f8d44f78a 100644 --- a/engine/src/flutter/third_party/txt/tests/paragraph_unittests.cc +++ b/engine/src/flutter/third_party/txt/tests/paragraph_unittests.cc @@ -1613,6 +1613,245 @@ TEST_F(ParagraphTest, ASSERT_TRUE(Snapshot()); } +TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeCenterParagraph)) { + const char* text = "01234   "; // includes ideographic space + // and english space. + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + paragraph_style.max_lines = 10; + paragraph_style.text_align = TextAlign::center; + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_family = "Roboto"; + text_style.font_size = 50; + text_style.letter_spacing = 0; + text_style.font_weight = FontWeight::w500; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = 1; + builder.PushStyle(text_style); + + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(550); + + paragraph->Paint(GetCanvas(), 0, 0); + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setAntiAlias(true); + paint.setStrokeWidth(1); + + // Tests for GetRectsForRange() + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; + paint.setColor(SK_ColorRED); + std::vector boxes = + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); + + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 203.95508); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 232.37305); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorBLUE); + boxes = + paragraph->GetRectsForRange(2, 4, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 260.79102); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 317.62695); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorGREEN); + boxes = + paragraph->GetRectsForRange(4, 6, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 2ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 317.62695); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 346.04492); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorBLACK); + boxes = + paragraph->GetRectsForRange(5, 6, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 346.04492); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 346.04492); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorRED); + boxes = + paragraph->GetRectsForRange(21, 21, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); + + ASSERT_TRUE(Snapshot()); +} + +TEST_F(ParagraphTest, + DISABLE_ON_WINDOWS(GetRectsForRangeCenterMultiLineParagraph)) { + const char* text = "01234   \n0123  "; // includes ideographic + // space and english space. + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + paragraph_style.max_lines = 10; + paragraph_style.text_align = TextAlign::center; + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_family = "Roboto"; + text_style.font_size = 50; + text_style.letter_spacing = 0; + text_style.font_weight = FontWeight::w500; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = 1; + builder.PushStyle(text_style); + + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(550); + + paragraph->Paint(GetCanvas(), 0, 0); + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setAntiAlias(true); + paint.setStrokeWidth(1); + + // Tests for GetRectsForRange() + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; + paint.setColor(SK_ColorRED); + std::vector boxes = + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); + + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 203.95508); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 232.37305); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorBLUE); + boxes = + paragraph->GetRectsForRange(2, 4, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 260.79102); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 317.62695); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorGREEN); + boxes = + paragraph->GetRectsForRange(4, 6, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 2ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 317.62695); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 346.04492); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorBLACK); + boxes = + paragraph->GetRectsForRange(5, 6, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 346.04492); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 346.04492); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorBLACK); + boxes = + paragraph->GetRectsForRange(10, 12, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 218.16406); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 59.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 275); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 118); + + paint.setColor(SK_ColorBLACK); + boxes = + paragraph->GetRectsForRange(14, 18, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 331.83594); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 59.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 331.83594); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 118); + + paint.setColor(SK_ColorRED); + boxes = + paragraph->GetRectsForRange(21, 21, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); + + ASSERT_TRUE(Snapshot()); +} + SkRect GetCoordinatesForGlyphPosition(const txt::Paragraph& paragraph, size_t pos) { std::vector boxes =