diff --git a/lib/stub_ui/lib/src/ui/text.dart b/lib/stub_ui/lib/src/ui/text.dart index 667ae0788a8ee..56d12935f0232 100644 --- a/lib/stub_ui/lib/src/ui/text.dart +++ b/lib/stub_ui/lib/src/ui/text.dart @@ -1089,6 +1089,14 @@ enum BoxHeightStyle { /// /// {@macro flutter.dart:ui.boxHeightStyle.includeLineSpacing} includeLineSpacingBottom, + + /// Calculate box heights based on the metrics of this paragraph's [StrutStyle]. + /// + /// Boxes based on the strut will have consistent heights throughout the + /// entire paragraph. The top edge of each line will align with the bottom + /// edge of the previous line. It is possible for glyphs to extend outside + /// these boxes. + strut, } /// Defines various ways to horizontally bound the boxes returned by diff --git a/lib/ui/text.dart b/lib/ui/text.dart index 4eab08a016358..b9e90f650810f 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -1270,6 +1270,14 @@ enum BoxHeightStyle { /// /// {@macro flutter.dart:ui.boxHeightStyle.includeLineSpacing} includeLineSpacingBottom, + + /// Calculate box heights based on the metrics of this paragraph's [StrutStyle]. + /// + /// Boxes based on the strut will have consistent heights throughout the + /// entire paragraph. The top edge of each line will align with the bottom + /// edge of the previous line. It is possible for glyphs to extend outside + /// these boxes. + strut, } /// Defines various ways to horizontally bound the boxes returned by diff --git a/third_party/txt/src/txt/paragraph.cc b/third_party/txt/src/txt/paragraph.cc index a75f0a064368d..ece9a31e26bfc 100644 --- a/third_party/txt/src/txt/paragraph.cc +++ b/third_party/txt/src/txt/paragraph.cc @@ -517,8 +517,7 @@ void Paragraph::Layout(double width, bool force) { double max_word_width = 0; // Compute strut minimums according to paragraph_style_. - StrutMetrics strut; - ComputeStrut(&strut, font); + ComputeStrut(&strut_, font); // Paragraph bounds tracking. size_t line_limit = std::min(paragraph_style_.max_lines, line_ranges_.size()); @@ -876,12 +875,12 @@ void Paragraph::Layout(double width, bool force) { // Calculate the amount to advance in the y direction. This is done by // computing the maximum ascent and descent with respect to the strut. - double max_ascent = strut.ascent + strut.half_leading; - double max_descent = strut.descent + strut.half_leading; + double max_ascent = strut_.ascent + strut_.half_leading; + double max_descent = strut_.descent + strut_.half_leading; SkScalar max_unscaled_ascent = 0; auto update_line_metrics = [&](const SkFontMetrics& metrics, const TextStyle& style) { - if (!strut.force_strut) { + if (!strut_.force_strut) { double ascent = (-metrics.fAscent + metrics.fLeading / 2) * style.height; max_ascent = std::max(ascent, max_ascent); @@ -1442,7 +1441,8 @@ std::vector Paragraph::GetRectsForRange( line_baselines_[kv.first] + line_max_descent_[kv.first]), box.direction); } - } else { // kIncludeLineSpacingBottom + } else if (rect_height_style == + RectHeightStyle::kIncludeLineSpacingBottom) { for (const Paragraph::TextBox& box : kv.second.boxes) { SkScalar adjusted_bottom = line_baselines_[kv.first] + line_max_descent_[kv.first]; @@ -1456,6 +1456,14 @@ std::vector Paragraph::GetRectsForRange( box.rect.fRight, adjusted_bottom), box.direction); } + } else if (rect_height_style == RectHeightStyle::kStrut) { + for (const Paragraph::TextBox& box : kv.second.boxes) { + boxes.emplace_back( + SkRect::MakeLTRB( + box.rect.fLeft, line_baselines_[kv.first] - strut_.ascent, + box.rect.fRight, line_baselines_[kv.first] + strut_.descent), + box.direction); + } } } return boxes; diff --git a/third_party/txt/src/txt/paragraph.h b/third_party/txt/src/txt/paragraph.h index 4dcaf2cf29f82..e47c2b41c9c31 100644 --- a/third_party/txt/src/txt/paragraph.h +++ b/third_party/txt/src/txt/paragraph.h @@ -79,7 +79,10 @@ class Paragraph { // The line spacing will be added to the top of the rect. kIncludeLineSpacingTop, // The line spacing will be added to the bottom of the rect. - kIncludeLineSpacingBottom + kIncludeLineSpacingBottom, + + // Calculate boxes based on the strut's metrics. + kStrut }; enum class RectWidthStyle { @@ -267,6 +270,18 @@ class Paragraph { std::vector line_baselines_; bool did_exceed_max_lines_; + // Strut metrics of zero will have no effect on the layout. + struct StrutMetrics { + double ascent = 0; // Positive value to keep signs clear. + double descent = 0; + double leading = 0; + double half_leading = 0; + double line_height = 0; + bool force_strut = false; + }; + + StrutMetrics strut_; + // Metrics for use in GetRectsForRange(...); // Per-line max metrics over all runs in a given line. std::vector line_max_spacings_; @@ -372,16 +387,6 @@ class Paragraph { : x_start(x_s), y_start(y_s), x_end(x_e), y_end(y_e) {} }; - // Strut metrics of zero will have no effect on the layout. - struct StrutMetrics { - double ascent = 0; // Positive value to keep signs clear. - double descent = 0; - double leading = 0; - double half_leading = 0; - double line_height = 0; - bool force_strut = false; - }; - // Passes in the text and Styled Runs. text_ and runs_ will later be passed // into breaker_ in InitBreaker(), which is called in Layout(). void SetText(std::vector text, StyledRuns runs); diff --git a/third_party/txt/tests/paragraph_unittests.cc b/third_party/txt/tests/paragraph_unittests.cc index 4b9a79b241bed..52b94a38e8bda 100644 --- a/third_party/txt/tests/paragraph_unittests.cc +++ b/third_party/txt/tests/paragraph_unittests.cc @@ -2200,6 +2200,65 @@ TEST_F(ParagraphTest, ASSERT_TRUE(Snapshot()); } +TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeStrut)) { + const char* text = "Chinese 字典"; + + 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.strut_enabled = true; + paragraph_style.strut_font_families.push_back("Roboto"); + paragraph_style.strut_font_size = 14; + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families.push_back("Noto Sans CJK JP"); + text_style.font_size = 20; + text_style.color = SK_ColorBLACK; + 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); + + std::vector strut_boxes = + paragraph->GetRectsForRange(0, 10, Paragraph::RectHeightStyle::kStrut, + Paragraph::RectWidthStyle::kMax); + ASSERT_EQ(strut_boxes.size(), 1ull); + const SkRect& strut_rect = strut_boxes.front().rect; + paint.setColor(SK_ColorRED); + GetCanvas()->drawRect(strut_rect, paint); + + std::vector tight_boxes = + paragraph->GetRectsForRange(0, 10, Paragraph::RectHeightStyle::kTight, + Paragraph::RectWidthStyle::kMax); + ASSERT_EQ(tight_boxes.size(), 1ull); + const SkRect& tight_rect = tight_boxes.front().rect; + paint.setColor(SK_ColorGREEN); + GetCanvas()->drawRect(tight_rect, paint); + + EXPECT_FLOAT_EQ(strut_rect.left(), 0); + EXPECT_FLOAT_EQ(strut_rect.top(), 10.611719); + EXPECT_FLOAT_EQ(strut_rect.right(), 118.60547); + EXPECT_FLOAT_EQ(strut_rect.bottom(), 27.017969); + + ASSERT_TRUE(tight_rect.contains(strut_rect)); + + ASSERT_TRUE(Snapshot()); +} + SkRect GetCoordinatesForGlyphPosition(const txt::Paragraph& paragraph, size_t pos) { std::vector boxes =