From db75633872212301a5967d896b9318a721a75d40 Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Mon, 25 Jan 2021 14:04:26 -0800 Subject: [PATCH 01/13] wip --- lib/ui/text.dart | 56 +++++--- lib/ui/text/paragraph_builder.cc | 31 +++-- third_party/txt/src/txt/paragraph_style.h | 18 --- third_party/txt/src/txt/paragraph_txt.cc | 153 ++++++++++++---------- third_party/txt/src/txt/text_style.cc | 4 + third_party/txt/src/txt/text_style.h | 22 ++++ 6 files changed, 165 insertions(+), 119 deletions(-) diff --git a/lib/ui/text.dart b/lib/ui/text.dart index b3d3b58347802..f394a79cd16bd 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -432,6 +432,11 @@ enum TextDecorationStyle { wavy } +enum TextBoxVerticalAlignment { + proportional, + centered, +} + /// {@template dart.ui.textHeightBehavior} /// Defines how the paragraph will apply [TextStyle.height] to the ascent of the /// first line and descent of the last line. @@ -456,14 +461,16 @@ class TextHeightBehavior { const TextHeightBehavior({ this.applyHeightToFirstAscent = true, this.applyHeightToLastDescent = true, + this.verticalAlignment = TextBoxVerticalAlignment.proportional, }); /// Creates a new TextHeightBehavior object from an encoded form. /// /// See [encode] for the creation of the encoded form. - const TextHeightBehavior.fromEncoded(int encoded) + TextHeightBehavior.fromEncoded(int encoded) : applyHeightToFirstAscent = (encoded & 0x1) == 0, - applyHeightToLastDescent = (encoded & 0x2) == 0; + applyHeightToLastDescent = (encoded & 0x2) == 0, + verticalAlignment = TextBoxVerticalAlignment.values[encoded >> 2]; /// Whether to apply the [TextStyle.height] modifier to the ascent of the first @@ -490,9 +497,13 @@ class TextHeightBehavior { /// Defaults to true (height modifications applied as normal). final bool applyHeightToLastDescent; + final TextBoxVerticalAlignment verticalAlignment; + /// Returns an encoded int representation of this object. int encode() { - return (applyHeightToFirstAscent ? 0 : 1 << 0) | (applyHeightToLastDescent ? 0 : 1 << 1); + return (applyHeightToFirstAscent ? 0 : 1 << 0) + | (applyHeightToLastDescent ? 0 : 1 << 1) + | (verticalAlignment.index << 2); } @override @@ -501,7 +512,8 @@ class TextHeightBehavior { return false; return other is TextHeightBehavior && other.applyHeightToFirstAscent == applyHeightToFirstAscent - && other.applyHeightToLastDescent == applyHeightToLastDescent; + && other.applyHeightToLastDescent == applyHeightToLastDescent + && other.verticalAlignment == verticalAlignment; } @override @@ -509,6 +521,7 @@ class TextHeightBehavior { return hashValues( applyHeightToFirstAscent, applyHeightToLastDescent, + verticalAlignment.index, ); } @@ -516,7 +529,8 @@ class TextHeightBehavior { String toString() { return 'TextHeightBehavior(' 'applyHeightToFirstAscent: $applyHeightToFirstAscent, ' - 'applyHeightToLastDescent: $applyHeightToLastDescent' + 'applyHeightToLastDescent: $applyHeightToLastDescent, ' + 'verticalAlignment: $verticalAlignment' ')'; } } @@ -578,13 +592,14 @@ Int32List _encodeTextStyle( double? letterSpacing, double? wordSpacing, double? height, + TextHeightBehavior? textHeightBehavior, Locale? locale, Paint? background, Paint? foreground, List? shadows, List? fontFeatures, ) { - final Int32List result = Int32List(8); + final Int32List result = Int32List(9); if (color != null) { result[0] |= 1 << 1; result[1] = color.value; @@ -613,49 +628,54 @@ Int32List _encodeTextStyle( result[0] |= 1 << 7; result[7] = textBaseline.index; } - if (decorationThickness != null) { + if (textHeightBehavior != null) { result[0] |= 1 << 8; + result[8] = textHeightBehavior.encode(); } - if (fontFamily != null || (fontFamilyFallback != null && fontFamilyFallback.isNotEmpty)) { + if (decorationThickness != null) { result[0] |= 1 << 9; + } + if (fontFamily != null || (fontFamilyFallback != null && fontFamilyFallback.isNotEmpty)) { + result[0] |= 1 << 10; // Passed separately to native. } if (fontSize != null) { - result[0] |= 1 << 10; + result[0] |= 1 << 11; // Passed separately to native. } if (letterSpacing != null) { - result[0] |= 1 << 11; + result[0] |= 1 << 12; // Passed separately to native. } if (wordSpacing != null) { - result[0] |= 1 << 12; + result[0] |= 1 << 13; // Passed separately to native. } if (height != null) { - result[0] |= 1 << 13; + result[0] |= 1 << 14; // Passed separately to native. } if (locale != null) { - result[0] |= 1 << 14; + result[0] |= 1 << 15; // Passed separately to native. } if (background != null) { - result[0] |= 1 << 15; + result[0] |= 1 << 16; // Passed separately to native. } if (foreground != null) { - result[0] |= 1 << 16; + result[0] |= 1 << 17; // Passed separately to native. } if (shadows != null) { - result[0] |= 1 << 17; + result[0] |= 1 << 18; // Passed separately to native. } if (fontFeatures != null) { - result[0] |= 1 << 18; + result[0] |= 1 << 19; // Passed separately to native. } + return result; } @@ -709,6 +729,7 @@ class TextStyle { double? letterSpacing, double? wordSpacing, double? height, + TextHeightBehavior? textHeightBehavior, Locale? locale, Paint? background, Paint? foreground, @@ -733,6 +754,7 @@ class TextStyle { letterSpacing, wordSpacing, height, + textHeightBehavior, locale, background, foreground, diff --git a/lib/ui/text/paragraph_builder.cc b/lib/ui/text/paragraph_builder.cc index 79dbe23b9957a..04adfa83c8c34 100644 --- a/lib/ui/text/paragraph_builder.cc +++ b/lib/ui/text/paragraph_builder.cc @@ -39,17 +39,18 @@ const int tsTextDecorationStyleIndex = 4; const int tsFontWeightIndex = 5; const int tsFontStyleIndex = 6; const int tsTextBaselineIndex = 7; -const int tsTextDecorationThicknessIndex = 8; -const int tsFontFamilyIndex = 9; -const int tsFontSizeIndex = 10; -const int tsLetterSpacingIndex = 11; -const int tsWordSpacingIndex = 12; -const int tsHeightIndex = 13; -const int tsLocaleIndex = 14; -const int tsBackgroundIndex = 15; -const int tsForegroundIndex = 16; -const int tsTextShadowsIndex = 17; -const int tsFontFeaturesIndex = 18; +const int tsTextHeightBehaviorIndex = 8; +const int tsTextDecorationThicknessIndex = 9; +const int tsFontFamilyIndex = 10; +const int tsFontSizeIndex = 11; +const int tsLetterSpacingIndex = 12; +const int tsWordSpacingIndex = 13; +const int tsHeightIndex = 14; +const int tsLocaleIndex = 15; +const int tsBackgroundIndex = 16; +const int tsForegroundIndex = 17; +const int tsTextShadowsIndex = 18; +const int tsFontFeaturesIndex = 19; const int tsColorMask = 1 << tsColorIndex; const int tsTextDecorationMask = 1 << tsTextDecorationIndex; @@ -59,6 +60,7 @@ const int tsTextDecorationThicknessMask = 1 << tsTextDecorationThicknessIndex; const int tsFontWeightMask = 1 << tsFontWeightIndex; const int tsFontStyleMask = 1 << tsFontStyleIndex; const int tsTextBaselineMask = 1 << tsTextBaselineIndex; +const int tsTextHeightBehaviorMask = 1 << tsTextHeightBehaviorIndex; const int tsFontFamilyMask = 1 << tsFontFamilyIndex; const int tsFontSizeMask = 1 << tsFontSizeIndex; const int tsLetterSpacingMask = 1 << tsLetterSpacingIndex; @@ -373,7 +375,7 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, Dart_Handle foreground_data, Dart_Handle shadows_data, Dart_Handle font_features_data) { - FML_DCHECK(encoded.num_elements() == 8); + FML_DCHECK(encoded.num_elements() == 9); int32_t mask = encoded[0]; @@ -410,6 +412,11 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, // property wasn't wired up either. } + style.has_text_height_behavior_override = mask & tsTextHeightBehaviorMask; + if (mask & tsTextHeightBehaviorMask) { + style.text_height_behavior = encoded[tsTextHeightBehaviorIndex]; + } + if (mask & (tsFontWeightMask | tsFontStyleMask | tsFontSizeMask | tsLetterSpacingMask | tsWordSpacingMask)) { if (mask & tsFontWeightMask) { diff --git a/third_party/txt/src/txt/paragraph_style.h b/third_party/txt/src/txt/paragraph_style.h index 46ea7a1e7c1a8..df9b862484c81 100644 --- a/third_party/txt/src/txt/paragraph_style.h +++ b/third_party/txt/src/txt/paragraph_style.h @@ -41,24 +41,6 @@ enum class TextDirection { ltr, }; -// Allows disabling height adjustments to first line's ascent and the -// last line's descent. If disabled, the line will use the default font -// metric provided ascent/descent and ParagraphStyle.height will not take -// effect. -// -// The default behavior is kAll where height adjustments are enabled for all -// lines. -// -// Multiple behaviors can be applied at once with a bitwise | operator. For -// example, disabling first ascent and last descent can achieved with: -// -// (kDisableFirstAscent | kDisableLastDescent). -enum TextHeightBehavior { - kAll = 0x0, - kDisableFirstAscent = 0x1, - kDisableLastDescent = 0x2, - kDisableAll = 0x1 | 0x2, -}; class ParagraphStyle { public: diff --git a/third_party/txt/src/txt/paragraph_txt.cc b/third_party/txt/src/txt/paragraph_txt.cc index 42943d22b8e16..5acae35836d31 100644 --- a/third_party/txt/src/txt/paragraph_txt.cc +++ b/third_party/txt/src/txt/paragraph_txt.cc @@ -1186,79 +1186,88 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics, size_t line_number, size_t line_limit) { if (!strut_.force_strut) { - double ascent; - double descent; - if (style.has_height_override) { - // Scale the ascent and descent such that the sum of ascent and - // descent is `fontsize * style.height * style.font_size`. - // - // The raw metrics do not add up to fontSize. The state of font - // metrics is a mess: - // - // Each font has 4 sets of vertical metrics: - // - // * hhea: hheaAscender, hheaDescender, hheaLineGap. - // Used by Apple. - // * OS/2 typo: typoAscender, typoDescender, typoLineGap. - // Used sometimes by Windows for layout. - // * OS/2 win: winAscent, winDescent. - // Also used by Windows, generally will be cut if extends past - // these metrics. - // * EM Square: ascent, descent - // Not actively used, but this defines the 'scale' of the - // units used. - // - // `Use Typo Metrics` is a boolean that, when enabled, prefers - // typo metrics over win metrics. Default is off. Enabled by most - // modern fonts. - // - // In addition to these different sets of metrics, there are also - // multiple strategies for using these metrics: - // - // * Adobe: Set hhea values to typo equivalents. - // * Microsoft: Set hhea values to win equivalents. - // * Web: Use hhea values for text, regardless of `Use Typo Metrics` - // The hheaLineGap is distributed half across the top and half - // across the bottom of the line. - // Exceptions: - // Windows: All browsers respect `Use Typo Metrics` - // Firefox respects `Use Typo Metrics`. - // - // This pertains to this code in that it is ambiguous which set of - // metrics we are actually using via SkFontMetrics. This in turn - // means that if we use the raw metrics, we will see differences - // between platforms as well as unpredictable line heights. - // - // A more thorough explanation is available at - // https://glyphsapp.com/tutorials/vertical-metrics - // - // Doing this ascent/descent normalization to the EM Square allows - // a sane, consistent, and reasonable line height to be specified, - // though it breaks with what is done by any of the platforms above. - double metrics_height = -metrics.fAscent + metrics.fDescent; - ascent = - (-metrics.fAscent / metrics_height) * style.height * style.font_size; - descent = - (metrics.fDescent / metrics_height) * style.height * style.font_size; - } else { - // Use the font-provided ascent, descent, and leading directly. - ascent = (-metrics.fAscent + metrics.fLeading / 2); - descent = (metrics.fDescent + metrics.fLeading / 2); - } - - // Account for text_height_behavior in paragraph_style_. + const double metrics_font_height = metrics.fDescent - metrics.fAscent; + // The overall height of the glyph blob. block_height = ascent + descent, + // unless one of kDisable{FirstAscent, LastDescent} is set. + const double block_height = style.has_height_override + ? style.height * style.font_size + : metrics_font_height + metrics.fLeading; + + // Scale the ascent and descent such that the sum of ascent and + // descent is `style.height * style.font_size`. // - // Disable first line ascent modifications. - if (line_number == 0 && paragraph_style_.text_height_behavior & - TextHeightBehavior::kDisableFirstAscent) { - ascent = -metrics.fAscent; - } - // Disable last line descent modifications. - if (line_number == line_limit - 1 && - paragraph_style_.text_height_behavior & - TextHeightBehavior::kDisableLastDescent) { - descent = metrics.fDescent; - } + // The raw metrics do not add up to fontSize. The state of font + // metrics is a mess: + // + // Each font has 4 sets of vertical metrics: + // + // * hhea: hheaAscender, hheaDescender, hheaLineGap. + // Used by Apple. + // * OS/2 typo: typoAscender, typoDescender, typoLineGap. + // Used sometimes by Windows for layout. + // * OS/2 win: winAscent, winDescent. + // Also used by Windows, generally will be cut if extends past + // these metrics. + // * EM Square: ascent, descent + // Not actively used, but this defines the 'scale' of the + // units used. + // + // `Use Typo Metrics` is a boolean that, when enabled, prefers + // typo metrics over win metrics. Default is off. Enabled by most + // modern fonts. + // + // In addition to these different sets of metrics, there are also + // multiple strategies for using these metrics: + // + // * Adobe: Set hhea values to typo equivalents. + // * Microsoft: Set hhea values to win equivalents. + // * Web: Use hhea values for text, regardless of `Use Typo Metrics` + // The hheaLineGap is distributed half across the top and half + // across the bottom of the line. + // Exceptions: + // Windows: All browsers respect `Use Typo Metrics` + // Firefox respects `Use Typo Metrics`. + // + // This pertains to this code in that it is ambiguous which set of + // metrics we are actually using via SkFontMetrics. This in turn + // means that if we use the raw metrics, we will see differences + // between platforms as well as unpredictable line heights. + // + // A more thorough explanation is available at + // https://glyphsapp.com/tutorials/vertical-metrics + // + // Doing this ascent/descent normalization to the EM Square allows + // a sane, consistent, and reasonable box height to be specified, + // though it breaks with what is done by any of the platforms above. + const bool shouldNormalizeFont = style.has_height_override; + const double font_height = shouldNormalizeFont + ? style.font_size + : metrics_font_height; + + const size_t text_height_behavior = style.has_text_height_behavior_override + ? style.text_height_behavior + : paragraph_style_.text_height_behavior; + + const double leading = text_height_behavior & TextHeightBehavior::kHalfLeading + ? std::max(block_height - font_height, 0.0) + : style.has_height_override ? 0 : metrics.fLeading; + + const double half_leading = leading / 2; + const double available_vspace = block_height - leading; + FML_DCHECK(available_vspace >= 0); + + const bool disableFirstAscent = line_number == 0 + && text_height_behavior & TextHeightBehavior::kDisableFirstAscent; + const bool disableLastDescent = line_number == line_limit - 1 && + text_height_behavior & TextHeightBehavior::kDisableLastDescent; + + // Proportionally distribute the remaining vertical space above and below + // the glyph blob's baseline, per the font's ascent/discent ratio. + const double modifiedAscent = -metrics.fAscent / metrics_font_height * available_vspace + half_leading; + double ascent = disableFirstAscent ? -metrics.fAscent : modifiedAscent; + double descent = disableLastDescent + ? metrics.fDescent + : block_height - modifiedAscent; ComputePlaceholder(placeholder_run, ascent, descent); diff --git a/third_party/txt/src/txt/text_style.cc b/third_party/txt/src/txt/text_style.cc index 04589659b6539..295ba8ac92b11 100644 --- a/third_party/txt/src/txt/text_style.cc +++ b/third_party/txt/src/txt/text_style.cc @@ -48,6 +48,10 @@ bool TextStyle::equals(const TextStyle& other) const { return false; if (has_height_override != other.has_height_override) return false; + if (has_text_height_behavior_override != other.has_text_height_behavior_override) + return false; + if (has_text_height_behavior_override && text_height_behavior != other.text_height_behavior) + return false; if (locale != other.locale) return false; if (foreground != other.foreground) diff --git a/third_party/txt/src/txt/text_style.h b/third_party/txt/src/txt/text_style.h index 0d4a92a099814..f5399cbfd2be3 100644 --- a/third_party/txt/src/txt/text_style.h +++ b/third_party/txt/src/txt/text_style.h @@ -31,6 +31,26 @@ namespace txt { +// Allows disabling height adjustments to first line's ascent and the +// last line's descent. If disabled, the line will use the default font +// metric provided ascent/descent and ParagraphStyle.height will not take +// effect. +// +// The default behavior is kAll where height adjustments are enabled for all +// lines. +// +// Multiple behaviors can be applied at once with a bitwise | operator. For +// example, disabling first ascent and last descent can achieved with: +// +// (kDisableFirstAscent | kDisableLastDescent). +enum TextHeightBehavior { + kAll = 0x0, + kDisableFirstAscent = 0x1, + kDisableLastDescent = 0x2, + kDisableAll = 0x1 | 0x2, + kHalfLeading = 0x1 << 2, +}; + class TextStyle { public: SkColor color = SK_ColorWHITE; @@ -44,6 +64,8 @@ class TextStyle { FontWeight font_weight = FontWeight::w400; FontStyle font_style = FontStyle::normal; TextBaseline text_baseline = TextBaseline::kAlphabetic; + size_t text_height_behavior = TextHeightBehavior::kAll; + bool has_text_height_behavior_override = false; // An ordered list of fonts in order of priority. The first font is more // highly preferred than the last font. std::vector font_families; From 401aca15b357d76faec9e846bebecbbf3fa36085 Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Tue, 26 Jan 2021 00:53:41 -0800 Subject: [PATCH 02/13] wip --- lib/ui/text.dart | 89 +++++++++++++++++------ lib/ui/text/paragraph_builder.cc | 11 ++- third_party/txt/src/txt/paragraph_style.h | 2 +- third_party/txt/src/txt/paragraph_txt.cc | 81 ++++++++++++--------- third_party/txt/src/txt/text_style.cc | 6 +- 5 files changed, 128 insertions(+), 61 deletions(-) diff --git a/lib/ui/text.dart b/lib/ui/text.dart index f394a79cd16bd..ed94ea3b31b2f 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -432,19 +432,43 @@ enum TextDecorationStyle { wavy } -enum TextBoxVerticalAlignment { +/// {@macro dart.ui.leadingDistribution} +enum LeadingDistribution { + /// Distributes the ["leading"](https://www.w3.org/TR/CSS2/visudet.html#leading) + /// space of the text proportionally above and below the text, to the + /// font's ascent/discent ratio. + /// + /// {@template dart.ui.leading} + /// The leading space of a text run is defined as + /// `TextStyle.height * TextStyle.fontSize - TextStyle.fontSize`. When + /// [TextStyle.height] is not set, the text run will always use the leading + /// space specified by the font instead. + /// {@endtemplate} proportional, - centered, + + /// Distributes the ["leading"](https://www.w3.org/TR/CSS2/visudet.html#leading) + /// space of the text evenly above and below the text (i.e. evenly above the + /// font's ascender and below the descender). + /// + /// {@macro dart.ui.leading} + /// + /// The leading space can become negative when [TextStyle.height] is set to + /// a value smaller than 1.0. + /// + /// This is the default strategy used by CSS, known as + /// ["half-leading"](https://www.w3.org/TR/css-inline-3/#half-leading). + even, } /// {@template dart.ui.textHeightBehavior} -/// Defines how the paragraph will apply [TextStyle.height] to the ascent of the -/// first line and descent of the last line. +/// Defines how to apply [TextStyle.height] over and under text. +/// +/// [applyHeightToFirstAscent] and [applyHeightToLastDescent] represent whether +/// the [TextStyle.height] modifier will be applied to the corresponding metric. +/// By default, all properties are true, and [TextStyle.height] is applied as +/// normal. When set to false, the font's default ascent will be used. +/// /// -/// Each boolean value represents whether the [TextStyle.height] modifier will -/// be applied to the corresponding metric. By default, all properties are true, -/// and [TextStyle.height] is applied as normal. When set to false, the font's -/// default ascent will be used. /// {@endtemplate} class TextHeightBehavior { @@ -461,7 +485,7 @@ class TextHeightBehavior { const TextHeightBehavior({ this.applyHeightToFirstAscent = true, this.applyHeightToLastDescent = true, - this.verticalAlignment = TextBoxVerticalAlignment.proportional, + this.leadingDistribution = LeadingDistribution.proportional, }); /// Creates a new TextHeightBehavior object from an encoded form. @@ -470,8 +494,7 @@ class TextHeightBehavior { TextHeightBehavior.fromEncoded(int encoded) : applyHeightToFirstAscent = (encoded & 0x1) == 0, applyHeightToLastDescent = (encoded & 0x2) == 0, - verticalAlignment = TextBoxVerticalAlignment.values[encoded >> 2]; - + leadingDistribution = LeadingDistribution.values[encoded >> 2]; /// Whether to apply the [TextStyle.height] modifier to the ascent of the first /// line in the paragraph. @@ -497,13 +520,24 @@ class TextHeightBehavior { /// Defaults to true (height modifications applied as normal). final bool applyHeightToLastDescent; - final TextBoxVerticalAlignment verticalAlignment; + /// {@template dart.ui.leadingDistribution} + /// How extra ["leading"](https://www.w3.org/TR/CSS2/visudet.html#leading) + /// space is distributed over and under the text. + /// + /// Does not affect layout when [TextStyle.height] is not specified. The + /// leading space can become negative, for example, when + /// [LeadingDistribution.even] is used with a [TextStyle.height] much + /// smaller than 1.0. + /// {@endtemplate} + /// + /// Defaults to [LeadingDistribution.proportional], + final LeadingDistribution leadingDistribution; /// Returns an encoded int representation of this object. int encode() { return (applyHeightToFirstAscent ? 0 : 1 << 0) | (applyHeightToLastDescent ? 0 : 1 << 1) - | (verticalAlignment.index << 2); + | (leadingDistribution.index << 2); } @override @@ -513,7 +547,7 @@ class TextHeightBehavior { return other is TextHeightBehavior && other.applyHeightToFirstAscent == applyHeightToFirstAscent && other.applyHeightToLastDescent == applyHeightToLastDescent - && other.verticalAlignment == verticalAlignment; + && other.leadingDistribution == leadingDistribution; } @override @@ -521,7 +555,7 @@ class TextHeightBehavior { return hashValues( applyHeightToFirstAscent, applyHeightToLastDescent, - verticalAlignment.index, + leadingDistribution.index, ); } @@ -530,7 +564,7 @@ class TextHeightBehavior { return 'TextHeightBehavior(' 'applyHeightToFirstAscent: $applyHeightToFirstAscent, ' 'applyHeightToLastDescent: $applyHeightToLastDescent, ' - 'verticalAlignment: $verticalAlignment' + 'leadingDistribution: $leadingDistribution' ')'; } } @@ -577,6 +611,8 @@ bool _listEquals(List? a, List? b) { // // - Element 7: The enum index of the |textBaseline|. // +// - Element 8: The encoded value of the |textHeightBehavior|. +// Int32List _encodeTextStyle( Color? color, TextDecoration? decoration, @@ -710,6 +746,7 @@ class TextStyle { /// * `textBaseline`: The common baseline that should be aligned between this text span and its parent text span, or, for the root text spans, with the line box. /// * `height`: The height of this text span, as a multiplier of the font size. Omitting `height` will allow the line height /// to take the height as defined by the font, which may not be exactly the height of the fontSize. + /// * `textHeightBehavior`: When `height` is specified, how the extra vertical space should be distributed over and under the text. /// * `locale`: The locale used to select region-specific glyphs. /// * `background`: The paint drawn as a background for the text. /// * `foreground`: The paint used to draw the text. If this is specified, `color` must be null. @@ -832,6 +869,7 @@ class TextStyle { 'letterSpacing: ${ _encoded[0] & 0x00800 == 0x00800 ? "${_letterSpacing}x" : "unspecified"}, ' 'wordSpacing: ${ _encoded[0] & 0x01000 == 0x01000 ? "${_wordSpacing}x" : "unspecified"}, ' 'height: ${ _encoded[0] & 0x02000 == 0x02000 ? "${_height}x" : "unspecified"}, ' + 'textHeightBehavior: ${ _encoded[0] & 0x0100 == 0x0100 ? "${TextHeightBehavior.fromEncoded(_encoded[8])}x" : "unspecified"}, ' 'locale: ${ _encoded[0] & 0x04000 == 0x04000 ? _locale : "unspecified"}, ' 'background: ${ _encoded[0] & 0x08000 == 0x08000 ? _background : "unspecified"}, ' 'foreground: ${ _encoded[0] & 0x10000 == 0x10000 ? _foreground : "unspecified"}, ' @@ -964,7 +1002,7 @@ class ParagraphStyle { /// be exactly the height of the `fontSize`. /// /// * `textHeightBehavior`: Specifies how the `height` multiplier is - /// applied to ascent of the first line and the descent of the last line. + /// applied to ascent and the descent of the text. /// /// * `fontWeight`: The typeface thickness to use when painting the text /// (e.g., bold). @@ -1081,6 +1119,7 @@ ByteData _encodeStrut( List? fontFamilyFallback, double? fontSize, double? height, + TextHeightBehavior? textHeightBehavior, double? leading, FontWeight? fontWeight, FontStyle? fontStyle, @@ -1088,13 +1127,14 @@ ByteData _encodeStrut( if (fontFamily == null && fontSize == null && height == null && + textHeightBehavior == null && leading == null && fontWeight == null && fontStyle == null && forceStrutHeight == null) return ByteData(0); - final ByteData data = ByteData(15); // Max size is 15 bytes + final ByteData data = ByteData(16); // Max size is 16 bytes int bitmask = 0; // 8 bit mask int byteCount = 1; if (fontWeight != null) { @@ -1120,17 +1160,22 @@ ByteData _encodeStrut( bitmask |= 1 << 4; data.setFloat32(byteCount, height, _kFakeHostEndian); byteCount += 4; + if (textHeightBehavior != null) { + bitmask |= 1 << 5; + data.setInt8(byteCount, textHeightBehavior.encode()); + byteCount += 4; + } } if (leading != null) { - bitmask |= 1 << 5; + bitmask |= 1 << 6; data.setFloat32(byteCount, leading, _kFakeHostEndian); byteCount += 4; } if (forceStrutHeight != null) { - bitmask |= 1 << 6; + bitmask |= 1 << 7; // We store this boolean directly in the bitmask since there is // extra space in the 16 bit int. - bitmask |= (forceStrutHeight ? 1 : 0) << 7; + bitmask |= (forceStrutHeight ? 1 : 0) << 8; } data.setInt8(0, bitmask); @@ -1185,6 +1230,7 @@ class StrutStyle { List? fontFamilyFallback, double? fontSize, double? height, + TextHeightBehavior? textHeightBehavior, double? leading, FontWeight? fontWeight, FontStyle? fontStyle, @@ -1194,6 +1240,7 @@ class StrutStyle { fontFamilyFallback, fontSize, height, + textHeightBehavior, leading, fontWeight, fontStyle, diff --git a/lib/ui/text/paragraph_builder.cc b/lib/ui/text/paragraph_builder.cc index 04adfa83c8c34..70da9f48cca04 100644 --- a/lib/ui/text/paragraph_builder.cc +++ b/lib/ui/text/paragraph_builder.cc @@ -120,14 +120,16 @@ const int sFontStyleIndex = 1; const int sFontFamilyIndex = 2; const int sFontSizeIndex = 3; const int sHeightIndex = 4; -const int sLeadingIndex = 5; -const int sForceStrutHeightIndex = 6; +const int sTextHeightBehaviorIndex = 5; +const int sLeadingIndex = 6; +const int sForceStrutHeightIndex = 7; const int sFontWeightMask = 1 << sFontWeightIndex; const int sFontStyleMask = 1 << sFontStyleIndex; const int sFontFamilyMask = 1 << sFontFamilyIndex; const int sFontSizeMask = 1 << sFontSizeIndex; const int sHeightMask = 1 << sHeightIndex; +const int stextHeightBehaviorMask = 1 << sTextHeightBehaviorIndex; const int sLeadingMask = 1 << sLeadingIndex; const int sForceStrutHeightMask = 1 << sForceStrutHeightIndex; @@ -212,6 +214,11 @@ void decodeStrut(Dart_Handle strut_data, if (mask & sHeightMask) { paragraph_style.strut_height = float_data[float_count++]; paragraph_style.strut_has_height_override = true; + + // TextHeightBehavior does not affect layout if height is not set. + if (mask & stextHeightBehaviorMask) { + paragraph_style.strut_text_height_behavior = uint8_data[byte_count]; + } } if (mask & sLeadingMask) { paragraph_style.strut_leading = float_data[float_count++]; diff --git a/third_party/txt/src/txt/paragraph_style.h b/third_party/txt/src/txt/paragraph_style.h index df9b862484c81..ce86ba2467174 100644 --- a/third_party/txt/src/txt/paragraph_style.h +++ b/third_party/txt/src/txt/paragraph_style.h @@ -41,7 +41,6 @@ enum class TextDirection { ltr, }; - class ParagraphStyle { public: // Default TextStyle. Used in GetTextStyle() to obtain the base TextStyle to @@ -64,6 +63,7 @@ class ParagraphStyle { double strut_font_size = 14; double strut_height = 1; bool strut_has_height_override = false; + size_t strut_text_height_behavior = TextHeightBehavior::kAll; double strut_leading = -1; // Negative to use font's default leading. [0,inf) // to use custom leading as a ratio of font size. bool force_strut_height = false; diff --git a/third_party/txt/src/txt/paragraph_txt.cc b/third_party/txt/src/txt/paragraph_txt.cc index 5acae35836d31..9f1f1ca9b364e 100644 --- a/third_party/txt/src/txt/paragraph_txt.cc +++ b/third_party/txt/src/txt/paragraph_txt.cc @@ -518,8 +518,8 @@ bool ParagraphTxt::IsStrutValid() const { } void ParagraphTxt::ComputeStrut(StrutMetrics* strut, SkFont& font) { - strut->ascent = 0; - strut->descent = 0; + strut->ascent = -FLT_MAX; + strut->descent = -FLT_MAX; strut->leading = 0; strut->half_leading = 0; strut->line_height = 0; @@ -553,20 +553,27 @@ void ParagraphTxt::ComputeStrut(StrutMetrics* strut, SkFont& font) { SkFontMetrics strut_metrics; font.getMetrics(&strut_metrics); + const double metrics_height = + -strut_metrics.fAscent + strut_metrics.fDescent; + if (paragraph_style_.strut_has_height_override) { - double metrics_height = -strut_metrics.fAscent + strut_metrics.fDescent; - strut->ascent = (-strut_metrics.fAscent / metrics_height) * - paragraph_style_.strut_height * - paragraph_style_.strut_font_size; - strut->descent = (strut_metrics.fDescent / metrics_height) * - paragraph_style_.strut_height * - paragraph_style_.strut_font_size; - strut->leading = - // Zero leading if there is no user specified strut leading. + const double strut_height = + paragraph_style_.strut_height * paragraph_style_.strut_font_size; + const double metrics_leading = + // Zero extra leading if there is no user specified strut leading. paragraph_style_.strut_leading < 0 ? 0 : (paragraph_style_.strut_leading * paragraph_style_.strut_font_size); + + const double available_height = paragraph_style_.strut_text_height_behavior & TextHeightBehavior::kHalfLeading + ? strut_height - metrics_height + : strut_height; + + strut->ascent = (-strut_metrics.fAscent / metrics_height) * available_height; + strut->descent = (strut_metrics.fDescent / metrics_height) * available_height; + + strut->leading = metrics_leading + strut_height - available_height; } else { strut->ascent = -strut_metrics.fAscent; strut->descent = strut_metrics.fDescent; @@ -671,7 +678,7 @@ void ParagraphTxt::Layout(double width) { glyph_lines_.clear(); code_unit_runs_.clear(); inline_placeholder_code_unit_runs_.clear(); - max_right_ = FLT_MIN; + max_right_ = -FLT_MAX; min_left_ = FLT_MAX; final_line_count_ = 0; @@ -1188,10 +1195,10 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics, if (!strut_.force_strut) { const double metrics_font_height = metrics.fDescent - metrics.fAscent; // The overall height of the glyph blob. block_height = ascent + descent, - // unless one of kDisable{FirstAscent, LastDescent} is set. - const double block_height = style.has_height_override - ? style.height * style.font_size - : metrics_font_height + metrics.fLeading; + // unless kDisableFirstAscent or kDisableLastDescent is set. + const double blob_height = style.has_height_override + ? style.height * style.font_size + : metrics_font_height + metrics.fLeading; // Scale the ascent and descent such that the sum of ascent and // descent is `style.height * style.font_size`. @@ -1237,37 +1244,41 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics, // https://glyphsapp.com/tutorials/vertical-metrics // // Doing this ascent/descent normalization to the EM Square allows - // a sane, consistent, and reasonable box height to be specified, + // a sane, consistent, and reasonable "blob_height" to be specified, // though it breaks with what is done by any of the platforms above. const bool shouldNormalizeFont = style.has_height_override; - const double font_height = shouldNormalizeFont - ? style.font_size - : metrics_font_height; + const double font_height = + shouldNormalizeFont ? style.font_size : metrics_font_height; - const size_t text_height_behavior = style.has_text_height_behavior_override - ? style.text_height_behavior - : paragraph_style_.text_height_behavior; + const size_t text_height_behavior = + style.has_text_height_behavior_override + ? style.text_height_behavior + : paragraph_style_.text_height_behavior; - const double leading = text_height_behavior & TextHeightBehavior::kHalfLeading - ? std::max(block_height - font_height, 0.0) - : style.has_height_override ? 0 : metrics.fLeading; + const double leading = + text_height_behavior & TextHeightBehavior::kHalfLeading + ? blob_height - font_height + //? std::max(block_height - font_height, 0.0) + : style.has_height_override ? 0 : metrics.fLeading; const double half_leading = leading / 2; - const double available_vspace = block_height - leading; - FML_DCHECK(available_vspace >= 0); + const double available_vspace = blob_height - leading; - const bool disableFirstAscent = line_number == 0 - && text_height_behavior & TextHeightBehavior::kDisableFirstAscent; - const bool disableLastDescent = line_number == line_limit - 1 && + const bool disableFirstAscent = + line_number == 0 && + text_height_behavior & TextHeightBehavior::kDisableFirstAscent; + const bool disableLastDescent = + line_number == line_limit - 1 && text_height_behavior & TextHeightBehavior::kDisableLastDescent; // Proportionally distribute the remaining vertical space above and below // the glyph blob's baseline, per the font's ascent/discent ratio. - const double modifiedAscent = -metrics.fAscent / metrics_font_height * available_vspace + half_leading; + const double modifiedAscent = + -metrics.fAscent / metrics_font_height * available_vspace + + half_leading; double ascent = disableFirstAscent ? -metrics.fAscent : modifiedAscent; - double descent = disableLastDescent - ? metrics.fDescent - : block_height - modifiedAscent; + double descent = + disableLastDescent ? metrics.fDescent : blob_height - modifiedAscent; ComputePlaceholder(placeholder_run, ascent, descent); diff --git a/third_party/txt/src/txt/text_style.cc b/third_party/txt/src/txt/text_style.cc index 295ba8ac92b11..1a279c55cf3ca 100644 --- a/third_party/txt/src/txt/text_style.cc +++ b/third_party/txt/src/txt/text_style.cc @@ -48,9 +48,11 @@ bool TextStyle::equals(const TextStyle& other) const { return false; if (has_height_override != other.has_height_override) return false; - if (has_text_height_behavior_override != other.has_text_height_behavior_override) + if (has_text_height_behavior_override != + other.has_text_height_behavior_override) return false; - if (has_text_height_behavior_override && text_height_behavior != other.text_height_behavior) + if (has_text_height_behavior_override && + text_height_behavior != other.text_height_behavior) return false; if (locale != other.locale) return false; From 26a16e85d0832704286395e3c248be863accab2d Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Mon, 1 Feb 2021 16:30:06 -0800 Subject: [PATCH 03/13] docs --- lib/ui/text.dart | 40 +++++++++++++----------- lib/web_ui/lib/src/ui/text.dart | 1 + third_party/txt/src/txt/paragraph_txt.cc | 16 ++++++---- third_party/txt/src/txt/text_style.h | 16 +++++++--- 4 files changed, 45 insertions(+), 28 deletions(-) diff --git a/lib/ui/text.dart b/lib/ui/text.dart index ed94ea3b31b2f..2b5962981565c 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -434,26 +434,26 @@ enum TextDecorationStyle { /// {@macro dart.ui.leadingDistribution} enum LeadingDistribution { - /// Distributes the ["leading"](https://www.w3.org/TR/CSS2/visudet.html#leading) - /// space of the text proportionally above and below the text, to the - /// font's ascent/discent ratio. + /// Distributes the [leading](https://en.wikipedia.org/wiki/Leading) + /// of the text proportionally above and below the text, to the font's + /// ascent/discent ratio. /// /// {@template dart.ui.leading} - /// The leading space of a text run is defined as + /// The leading of a text run is defined as /// `TextStyle.height * TextStyle.fontSize - TextStyle.fontSize`. When - /// [TextStyle.height] is not set, the text run will always use the leading - /// space specified by the font instead. + /// [TextStyle.height] is not set, the text run uses the leading specified by + /// the font instead. /// {@endtemplate} proportional, - /// Distributes the ["leading"](https://www.w3.org/TR/CSS2/visudet.html#leading) - /// space of the text evenly above and below the text (i.e. evenly above the + /// Distributes the ["leading"](https://en.wikipedia.org/wiki/Leading) + /// of the text evenly above and below the text (i.e. evenly above the /// font's ascender and below the descender). /// /// {@macro dart.ui.leading} /// - /// The leading space can become negative when [TextStyle.height] is set to - /// a value smaller than 1.0. + /// The leading can become negative when [TextStyle.height] is smaller than + /// 1.0. /// /// This is the default strategy used by CSS, known as /// ["half-leading"](https://www.w3.org/TR/css-inline-3/#half-leading). @@ -465,9 +465,12 @@ enum LeadingDistribution { /// /// [applyHeightToFirstAscent] and [applyHeightToLastDescent] represent whether /// the [TextStyle.height] modifier will be applied to the corresponding metric. -/// By default, all properties are true, and [TextStyle.height] is applied as +/// By default both properties are true, and [TextStyle.height] is applied as /// normal. When set to false, the font's default ascent will be used. /// +/// [leadingDistribution] determines how the [leading] is distributed over and +/// under text. This property applies before [applyHeightToFirstAscent] and +/// [applyHeightToLastDescent]. /// /// {@endtemplate} class TextHeightBehavior { @@ -480,6 +483,8 @@ class TextHeightBehavior { /// * applyHeightToLastDescent: When true, the [TextStyle.height] modifier /// will be applied to the descent of the last line. When false, the font's /// default descent will be used. + /// * leadingDistribution: How the [leading] is distributed over and under + /// text. /// /// All properties default to true (height modifications applied as normal). const TextHeightBehavior({ @@ -521,13 +526,12 @@ class TextHeightBehavior { final bool applyHeightToLastDescent; /// {@template dart.ui.leadingDistribution} - /// How extra ["leading"](https://www.w3.org/TR/CSS2/visudet.html#leading) - /// space is distributed over and under the text. + /// How the ["leading"](https://en.wikipedia.org/wiki/Leading) is distributed + /// over and under the text. /// /// Does not affect layout when [TextStyle.height] is not specified. The - /// leading space can become negative, for example, when - /// [LeadingDistribution.even] is used with a [TextStyle.height] much - /// smaller than 1.0. + /// leading can become negative, for example, when [LeadingDistribution.even] + /// is used with a [TextStyle.height] much smaller than 1.0. /// {@endtemplate} /// /// Defaults to [LeadingDistribution.proportional], @@ -864,12 +868,12 @@ class TextStyle { && _fontFamily != '' ? _fontFamily : "unspecified"}, ' 'fontFamilyFallback: ${ _encoded[0] & 0x00200 == 0x00200 && _fontFamilyFallback != null - && _fontFamilyFallback!.isNotEmpty ? _fontFamilyFallback : "unspecified"}, ' + && _fontFamilyFallback!.isNotEmpty ? _fontFamilyFallback : "unspecified"}, ' 'fontSize: ${ _encoded[0] & 0x00400 == 0x00400 ? _fontSize : "unspecified"}, ' 'letterSpacing: ${ _encoded[0] & 0x00800 == 0x00800 ? "${_letterSpacing}x" : "unspecified"}, ' 'wordSpacing: ${ _encoded[0] & 0x01000 == 0x01000 ? "${_wordSpacing}x" : "unspecified"}, ' 'height: ${ _encoded[0] & 0x02000 == 0x02000 ? "${_height}x" : "unspecified"}, ' - 'textHeightBehavior: ${ _encoded[0] & 0x0100 == 0x0100 ? "${TextHeightBehavior.fromEncoded(_encoded[8])}x" : "unspecified"}, ' + 'textHeightBehavior: ${ _encoded[0] & 0x0100 == 0x0100 ? "${TextHeightBehavior.fromEncoded(_encoded[8])}" : "unspecified"}, ' 'locale: ${ _encoded[0] & 0x04000 == 0x04000 ? _locale : "unspecified"}, ' 'background: ${ _encoded[0] & 0x08000 == 0x08000 ? _background : "unspecified"}, ' 'foreground: ${ _encoded[0] & 0x10000 == 0x10000 ? _foreground : "unspecified"}, ' diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index 43c54c4e9b6d2..4659b903193ed 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -248,6 +248,7 @@ abstract class TextStyle { double? letterSpacing, double? wordSpacing, double? height, + TextHeightBehavior? textHeightBehavior, Locale? locale, Paint? background, Paint? foreground, diff --git a/third_party/txt/src/txt/paragraph_txt.cc b/third_party/txt/src/txt/paragraph_txt.cc index 9f1f1ca9b364e..3a13c0491b92e 100644 --- a/third_party/txt/src/txt/paragraph_txt.cc +++ b/third_party/txt/src/txt/paragraph_txt.cc @@ -566,12 +566,16 @@ void ParagraphTxt::ComputeStrut(StrutMetrics* strut, SkFont& font) { : (paragraph_style_.strut_leading * paragraph_style_.strut_font_size); - const double available_height = paragraph_style_.strut_text_height_behavior & TextHeightBehavior::kHalfLeading - ? strut_height - metrics_height - : strut_height; - - strut->ascent = (-strut_metrics.fAscent / metrics_height) * available_height; - strut->descent = (strut_metrics.fDescent / metrics_height) * available_height; + const double available_height = + paragraph_style_.strut_text_height_behavior & + TextHeightBehavior::kHalfLeading + ? strut_height - metrics_height + : strut_height; + + strut->ascent = + (-strut_metrics.fAscent / metrics_height) * available_height; + strut->descent = + (strut_metrics.fDescent / metrics_height) * available_height; strut->leading = metrics_leading + strut_height - available_height; } else { diff --git a/third_party/txt/src/txt/text_style.h b/third_party/txt/src/txt/text_style.h index f5399cbfd2be3..dc818ef3d7b83 100644 --- a/third_party/txt/src/txt/text_style.h +++ b/third_party/txt/src/txt/text_style.h @@ -31,10 +31,18 @@ namespace txt { -// Allows disabling height adjustments to first line's ascent and the -// last line's descent. If disabled, the line will use the default font -// metric provided ascent/descent and ParagraphStyle.height will not take -// effect. +// Adjusts the leading over and under text. +// +// kDisableFirstAscent and kDisableLastDescent allow disabling height +// adjustments to first line's ascent and the last line's descent. If disabled, +// the line will use the default font metric provided ascent/descent and +// ParagraphStyle.height or TextStyle.height will not take effect. +// +// kHalfLeading determines how the leading is distributed over and under the +// text. When true, half of the leading is added to the top of the text and the +// other half is added to the bottom of the text. Otherwise, instead of +// distributing the space evenly, it's distributed proportionally to the font's +// ascent/descent ratio. // // The default behavior is kAll where height adjustments are enabled for all // lines. From 63f5fbf6f628374c180a6d8afa310f41d0ff5d5e Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Mon, 1 Feb 2021 21:30:26 -0800 Subject: [PATCH 04/13] fix tests --- testing/dart/text_test.dart | 16 +++++----- third_party/txt/src/txt/paragraph_txt.cc | 39 +++++++++++++----------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/testing/dart/text_test.dart b/testing/dart/text_test.dart index 559aeef5394d3..5f168a5af2ffc 100644 --- a/testing/dart/text_test.dart +++ b/testing/dart/text_test.dart @@ -77,17 +77,17 @@ void main() { }); test('decode works', () { - expect(const TextHeightBehavior.fromEncoded(0), equals(behavior0)); - expect(const TextHeightBehavior.fromEncoded(3), equals(behavior1)); - expect(const TextHeightBehavior.fromEncoded(1), equals(behavior2)); - expect(const TextHeightBehavior.fromEncoded(2), equals(behavior3)); + expect(TextHeightBehavior.fromEncoded(0), equals(behavior0)); + expect(TextHeightBehavior.fromEncoded(3), equals(behavior1)); + expect(TextHeightBehavior.fromEncoded(1), equals(behavior2)); + expect(TextHeightBehavior.fromEncoded(2), equals(behavior3)); }); test('toString works', () { - expect(behavior0.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: true)')); - expect(behavior1.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: false)')); - expect(behavior2.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: true)')); - expect(behavior3.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false)')); + expect(behavior0.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: true, leadingDistribution: LeadingDistribution.proportional)')); + expect(behavior1.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: false, leadingDistribution: LeadingDistribution.proportional)')); + expect(behavior2.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: true, leadingDistribution: LeadingDistribution.proportional)')); + expect(behavior3.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false, leadingDistribution: LeadingDistribution.proportional)')); }); }); diff --git a/third_party/txt/src/txt/paragraph_txt.cc b/third_party/txt/src/txt/paragraph_txt.cc index 3a13c0491b92e..7f9eb7475907b 100644 --- a/third_party/txt/src/txt/paragraph_txt.cc +++ b/third_party/txt/src/txt/paragraph_txt.cc @@ -1198,11 +1198,17 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics, size_t line_limit) { if (!strut_.force_strut) { const double metrics_font_height = metrics.fDescent - metrics.fAscent; - // The overall height of the glyph blob. block_height = ascent + descent, - // unless kDisableFirstAscent or kDisableLastDescent is set. + // The overall height of the glyph blob. If neither the ascent and the + // descent is disabled, we have block_height = ascent + descent, where + // "ascent" is the extent from the top of the blob to its baseline, and + // "descent" is the extent from the text blob's baseline to its bottom. const double blob_height = style.has_height_override ? style.height * style.font_size : metrics_font_height + metrics.fLeading; + const size_t text_height_behavior = + style.has_text_height_behavior_override + ? style.text_height_behavior + : paragraph_style_.text_height_behavior; // Scale the ascent and descent such that the sum of ascent and // descent is `style.height * style.font_size`. @@ -1250,28 +1256,24 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics, // Doing this ascent/descent normalization to the EM Square allows // a sane, consistent, and reasonable "blob_height" to be specified, // though it breaks with what is done by any of the platforms above. - const bool shouldNormalizeFont = style.has_height_override; + const bool shouldNormalizeFont = + style.has_height_override && + (text_height_behavior & TextHeightBehavior::kHalfLeading); const double font_height = shouldNormalizeFont ? style.font_size : metrics_font_height; - const size_t text_height_behavior = - style.has_text_height_behavior_override - ? style.text_height_behavior - : paragraph_style_.text_height_behavior; - const double leading = text_height_behavior & TextHeightBehavior::kHalfLeading ? blob_height - font_height - //? std::max(block_height - font_height, 0.0) - : style.has_height_override ? 0 : metrics.fLeading; + : style.has_height_override ? 0.0 : metrics.fLeading; const double half_leading = leading / 2; const double available_vspace = blob_height - leading; - const bool disableFirstAscent = + const bool disableAscent = line_number == 0 && text_height_behavior & TextHeightBehavior::kDisableFirstAscent; - const bool disableLastDescent = + const bool disableDescent = line_number == line_limit - 1 && text_height_behavior & TextHeightBehavior::kDisableLastDescent; @@ -1280,9 +1282,12 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics, const double modifiedAscent = -metrics.fAscent / metrics_font_height * available_vspace + half_leading; - double ascent = disableFirstAscent ? -metrics.fAscent : modifiedAscent; - double descent = - disableLastDescent ? metrics.fDescent : blob_height - modifiedAscent; + double ascent = disableAscent ? -metrics.fAscent : modifiedAscent; + double descent = disableDescent ? metrics.fDescent + //: blob_height - leading - modifiedAscent; + : metrics.fDescent / metrics_font_height * + available_vspace + + half_leading; ComputePlaceholder(placeholder_run, ascent, descent); @@ -1666,7 +1671,7 @@ std::vector ParagraphTxt::GetRectsForRange( // Per-line metrics for max and min coordinates for left and right boxes. // These metrics cannot be calculated in layout generically because of // selections that do not cover the whole line. - SkScalar max_right = FLT_MIN; + SkScalar max_right = -FLT_MAX; SkScalar min_left = FLT_MAX; }; @@ -1944,7 +1949,7 @@ std::vector ParagraphTxt::GetRectsForPlaceholders() { // Per-line metrics for max and min coordinates for left and right boxes. // These metrics cannot be calculated in layout generically because of // selections that do not cover the whole line. - SkScalar max_right = FLT_MIN; + SkScalar max_right = -FLT_MAX; SkScalar min_left = FLT_MAX; }; From 45ce33f45d79a986f162a94c27bda525e865acac Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Tue, 2 Feb 2021 17:19:47 -0800 Subject: [PATCH 05/13] more tests --- third_party/txt/src/txt/paragraph_txt.cc | 4 +- third_party/txt/tests/paragraph_unittests.cc | 485 +++++++++++++++++++ 2 files changed, 487 insertions(+), 2 deletions(-) diff --git a/third_party/txt/src/txt/paragraph_txt.cc b/third_party/txt/src/txt/paragraph_txt.cc index 7f9eb7475907b..8f003bc928878 100644 --- a/third_party/txt/src/txt/paragraph_txt.cc +++ b/third_party/txt/src/txt/paragraph_txt.cc @@ -569,7 +569,7 @@ void ParagraphTxt::ComputeStrut(StrutMetrics* strut, SkFont& font) { const double available_height = paragraph_style_.strut_text_height_behavior & TextHeightBehavior::kHalfLeading - ? strut_height - metrics_height + ? metrics_height : strut_height; strut->ascent = @@ -1258,7 +1258,7 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics, // though it breaks with what is done by any of the platforms above. const bool shouldNormalizeFont = style.has_height_override && - (text_height_behavior & TextHeightBehavior::kHalfLeading); + !(text_height_behavior & TextHeightBehavior::kHalfLeading); const double font_height = shouldNormalizeFont ? style.font_size : metrics_font_height; diff --git a/third_party/txt/tests/paragraph_unittests.cc b/third_party/txt/tests/paragraph_unittests.cc index 188c5f170bb6d..71973f0b4ee79 100644 --- a/third_party/txt/tests/paragraph_unittests.cc +++ b/third_party/txt/tests/paragraph_unittests.cc @@ -1883,6 +1883,491 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(HeightOverrideParagraph)) { ASSERT_TRUE(Snapshot()); } +TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(HeightOverrideHalfLeadingParagraph)) { + // All 3 lines will have the same typeface. + const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可"; + 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_height_behavior = TextHeightBehavior::kHalfLeading; + txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 20; + text_style.letter_spacing = 0; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = 3.6345; + text_style.has_height_override = true; + // Disables text height behavior override so it should defaults to the + // paragraph style. + text_style.has_text_height_behavior_override = false; + text_style.text_height_behavior = TextHeightBehavior::kAll; + builder.PushStyle(text_style); + + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = BuildParagraph(builder); + 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::kTight; + 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, 40, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + // With half-leadding, the x coordinates should remain the same but the glyphs + // are shifted up (as compared to AD-scaling). + EXPECT_EQ(boxes.size(), 3ull); + + const double line_spacing1 = boxes[1].rect.top() - boxes[0].rect.bottom(); + const double line_spacing2 = boxes[2].rect.top() - boxes[1].rect.bottom(); + + EXPECT_EQ(line_spacing1, line_spacing2); + // half leading. + EXPECT_NEAR(line_spacing1 * 0.5, boxes[0].rect.top(), 0.5); + + EXPECT_FLOAT_EQ(boxes[1].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[1].rect.right(), 43.851562); + + ASSERT_TRUE(Snapshot()); +} + +TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(HeightOverrideHalfLeadingTextStyle)) { + // All 3 lines will have the same typeface. + const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可"; + 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_height_behavior = TextHeightBehavior::kAll; + txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 20; + text_style.letter_spacing = 0; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = 3.6345; + text_style.has_height_override = true; + // Override paragraph_style.text_height_behavior: + text_style.has_text_height_behavior_override = true; + text_style.text_height_behavior = TextHeightBehavior::kHalfLeading; + builder.PushStyle(text_style); + + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = BuildParagraph(builder); + 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::kTight; + Paragraph::RectHeightStyle rect_height_style_max = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; + paint.setColor(SK_ColorRED); + + std::vector boxes = + paragraph->GetRectsForRange(0, 40, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + + std::vector line_boxes = paragraph->GetRectsForRange( + 0, 40, rect_height_style_max, rect_width_style); + EXPECT_EQ(boxes.size(), 3ull); + EXPECT_EQ(line_boxes.size(), 3ull); + + const double line_spacing1 = boxes[1].rect.top() - boxes[0].rect.bottom(); + const double line_spacing2 = boxes[2].rect.top() - boxes[1].rect.bottom(); + + EXPECT_EQ(line_spacing1, line_spacing2); + + // half leading. + EXPECT_EQ(line_boxes[0].rect.top() - boxes[0].rect.top(), + boxes[0].rect.bottom() - line_boxes[0].rect.bottom()); + EXPECT_EQ(line_boxes[1].rect.top() - boxes[1].rect.top(), + boxes[1].rect.bottom() - line_boxes[1].rect.bottom()); + EXPECT_EQ(line_boxes[2].rect.top() - boxes[2].rect.top(), + boxes[2].rect.bottom() - line_boxes[2].rect.bottom()); + // With half-leadding, the x coordinates should remain the same. + EXPECT_FLOAT_EQ(boxes[1].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[1].rect.right(), 43.851562); + + ASSERT_TRUE(Snapshot()); +} + +TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(MixedTextHeightBehaviorSameLine)) { + // Both runs will still have the same typeface, but with different text height + // behaviors. + const char* text = "01234満毎冠行来昼本可abcd"; + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + std::u16string u16_text2(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + paragraph_style.max_lines = 10; + paragraph_style.text_height_behavior = TextHeightBehavior::kAll; + txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 20; + text_style.letter_spacing = 0; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = 3.6345; + text_style.has_height_override = true; + // First run, with half-leading. + text_style.has_text_height_behavior_override = true; + text_style.text_height_behavior = TextHeightBehavior::kHalfLeading; + builder.PushStyle(text_style); + builder.AddText(u16_text); + + // Second run with AD-scaling. + text_style.has_text_height_behavior_override = true; + text_style.text_height_behavior = TextHeightBehavior::kAll; + + builder.PushStyle(text_style); + builder.AddText(u16_text2); + builder.Pop(); + + auto paragraph = BuildParagraph(builder); + 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::kTight; + Paragraph::RectHeightStyle rect_height_style_max = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; + paint.setColor(SK_ColorRED); + + std::vector boxes = paragraph->GetRectsForRange( + 0, icu_text.length(), rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + + std::vector line_boxes = paragraph->GetRectsForRange( + 0, icu_text.length(), rect_height_style_max, rect_width_style); + // The runs has the same typeface so they should be grouped together. + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_EQ(line_boxes.size(), 1ull); + + const double glyphHeight = boxes[0].rect.height(); + const double metricsAscent = 18.5546875; + const double metricsDescent = 4.8828125; + EXPECT_DOUBLE_EQ(glyphHeight, metricsAscent + metricsDescent); + + const double line_height = 3.6345 * 20; + const double leading = line_height - glyphHeight; + + // Overall descent is from half-leading and overall ascent is from AD-scaling. + EXPECT_NEAR(boxes[0].rect.top() - line_boxes[0].rect.top(), + leading * metricsAscent / (metricsAscent + metricsDescent), + 0.001); + + EXPECT_NEAR(line_boxes[0].rect.bottom() - boxes[0].rect.bottom(), + leading * 0.5, 0.001); +} + +TEST_F(ParagraphTest, + DISABLE_ON_WINDOWS(MixedTextHeightBehaviorSameLineWithZeroHeight)) { + // Both runs will still have the same typeface, but with different text height + // behaviors. + const char* text = "01234満毎冠行来昼本可abcd"; + 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_height_behavior = TextHeightBehavior::kAll; + txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 20; + text_style.letter_spacing = 0; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + // Set height to 0 + text_style.height = 0; + text_style.has_height_override = true; + // First run, with half-leading. + text_style.has_text_height_behavior_override = true; + text_style.text_height_behavior = TextHeightBehavior::kHalfLeading; + builder.PushStyle(text_style); + builder.AddText(u16_text); + + // Second run with AD-scaling. + text_style.has_text_height_behavior_override = true; + text_style.text_height_behavior = TextHeightBehavior::kAll; + + builder.PushStyle(text_style); + builder.AddText(u16_text); + builder.Pop(); + + auto paragraph = BuildParagraph(builder); + 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::kTight; + Paragraph::RectHeightStyle rect_height_style_max = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; + paint.setColor(SK_ColorRED); + + std::vector boxes = paragraph->GetRectsForRange( + 0, icu_text.length(), rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + + std::vector line_boxes = paragraph->GetRectsForRange( + 0, icu_text.length(), rect_height_style_max, rect_width_style); + // The runs has the same typeface so they should be grouped together. + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_EQ(line_boxes.size(), 1ull); + + const double glyphHeight = boxes[0].rect.height(); + const double metricsAscent = 18.5546875; + const double metricsDescent = 4.8828125; + EXPECT_DOUBLE_EQ(glyphHeight, metricsAscent + metricsDescent); + + // line_height for both styled runs is 0, but the overall line height is not + // 0. + EXPECT_DOUBLE_EQ(line_boxes[0].rect.height(), + metricsAscent - (metricsAscent + metricsDescent) / 2); + EXPECT_LT(boxes[0].rect.top(), 0.0); + EXPECT_GT(boxes[0].rect.bottom(), 0.0); +} + +TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(HeightOverrideHalfLeadingStrut)) { + // All 3 lines will have the same typeface. + const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可"; + 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.strut_enabled = true; + paragraph_style.strut_has_height_override = true; + paragraph_style.strut_height = 3.6345; + paragraph_style.strut_font_size = 20; + paragraph_style.strut_font_families.push_back("Roboto"); + paragraph_style.strut_text_height_behavior = TextHeightBehavior::kHalfLeading; + txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 20; + text_style.letter_spacing = 0; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = 3.6345; + text_style.has_height_override = true; + // Override paragraph_style.text_height_behavior: + text_style.has_text_height_behavior_override = true; + text_style.text_height_behavior = TextHeightBehavior::kHalfLeading; + builder.PushStyle(text_style); + + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = BuildParagraph(builder); + 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::kTight; + Paragraph::RectHeightStyle rect_height_style_max = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; + paint.setColor(SK_ColorRED); + + std::vector boxes = + paragraph->GetRectsForRange(0, 40, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + + std::vector line_boxes = paragraph->GetRectsForRange( + 0, 40, rect_height_style_max, rect_width_style); + EXPECT_EQ(boxes.size(), 3ull); + EXPECT_EQ(line_boxes.size(), 3ull); + + const double line_spacing1 = boxes[1].rect.top() - boxes[0].rect.bottom(); + const double line_spacing2 = boxes[2].rect.top() - boxes[1].rect.bottom(); + + EXPECT_EQ(line_spacing1, line_spacing2); + + // Strut half leading. + EXPECT_EQ(line_boxes[0].rect.top() - boxes[0].rect.top(), + boxes[0].rect.bottom() - line_boxes[0].rect.bottom()); + EXPECT_EQ(line_boxes[1].rect.top() - boxes[1].rect.top(), + boxes[1].rect.bottom() - line_boxes[1].rect.bottom()); + EXPECT_EQ(line_boxes[2].rect.top() - boxes[2].rect.top(), + boxes[2].rect.bottom() - line_boxes[2].rect.bottom()); + + EXPECT_FLOAT_EQ(boxes[1].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[1].rect.right(), 43.851562); + + ASSERT_TRUE(Snapshot()); +} + +TEST_F(ParagraphTest, + DISABLE_ON_WINDOWS(ZeroHeightHalfLeadingStrutForceHeight)) { + // All 3 lines will have the same typeface. + const char* text = "01234満毎冠行来昼本可abcdn満毎冠行来昼本可"; + 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.strut_enabled = true; + paragraph_style.strut_has_height_override = true; + paragraph_style.strut_height = 0; + // Force strut height. + paragraph_style.force_strut_height = true; + paragraph_style.strut_font_size = 20; + paragraph_style.strut_font_families.push_back("Roboto"); + paragraph_style.strut_text_height_behavior = TextHeightBehavior::kHalfLeading; + txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 20; + text_style.letter_spacing = 0; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = 0; + text_style.has_height_override = true; + + // First run, with half-leading. + text_style.has_text_height_behavior_override = true; + text_style.text_height_behavior = TextHeightBehavior::kHalfLeading; + builder.PushStyle(text_style); + builder.AddText(u16_text); + + // Second run with AD-scaling. + text_style.has_text_height_behavior_override = true; + text_style.text_height_behavior = TextHeightBehavior::kAll; + + builder.PushStyle(text_style); + builder.AddText(u16_text); + builder.Pop(); + + auto paragraph = BuildParagraph(builder); + 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::kTight; + Paragraph::RectHeightStyle rect_height_style_max = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; + paint.setColor(SK_ColorRED); + + std::vector boxes = paragraph->GetRectsForRange( + 0, icu_text.length(), rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + + std::vector line_boxes = paragraph->GetRectsForRange( + 0, icu_text.length(), rect_height_style_max, rect_width_style); + // The runs has the same typeface so they should be grouped together. + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_EQ(line_boxes.size(), 1ull); + + const double glyphHeight = boxes[0].rect.height(); + const double metricsAscent = 18.5546875; + const double metricsDescent = 4.8828125; + EXPECT_DOUBLE_EQ(glyphHeight, metricsAscent + metricsDescent); + + EXPECT_DOUBLE_EQ(line_boxes[0].rect.height(), 0.0); + + ASSERT_TRUE(Snapshot()); +} + TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(LeftAlignParagraph)) { const char* text = "This is a very long sentence to test if the text will properly wrap " From c0b1bef64edf8d2e911ffa25abe6b583fb2f42a9 Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Tue, 2 Feb 2021 19:42:53 -0800 Subject: [PATCH 06/13] fix tests --- lib/web_ui/lib/src/engine/text/paragraph.dart | 6 ++++++ lib/web_ui/lib/src/ui/text.dart | 16 +++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 67fec31b85de6..f9df5341d06a9 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -1209,6 +1209,8 @@ class EngineStrutStyle implements ui.StrutStyle { List? fontFamilyFallback, double? fontSize, double? height, + //TODO(LongCatIsLooong): implement textHeightBehavir. + ui.TextHeightBehavior? textHeightBehavior, double? leading, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, @@ -1217,6 +1219,7 @@ class EngineStrutStyle implements ui.StrutStyle { _fontFamilyFallback = fontFamilyFallback, _fontSize = fontSize, _height = height, + _textHeightBehavior = textHeightBehavior, _leading = leading, _fontWeight = fontWeight, _fontStyle = fontStyle, @@ -1230,6 +1233,7 @@ class EngineStrutStyle implements ui.StrutStyle { final ui.FontWeight? _fontWeight; final ui.FontStyle? _fontStyle; final bool? _forceStrutHeight; + final ui.TextHeightBehavior? _textHeightBehavior; @override bool operator ==(Object other) { @@ -1243,6 +1247,7 @@ class EngineStrutStyle implements ui.StrutStyle { && other._fontFamily == _fontFamily && other._fontSize == _fontSize && other._height == _height + && other._textHeightBehavior == _textHeightBehavior && other._leading == _leading && other._fontWeight == _fontWeight && other._fontStyle == _fontStyle @@ -1256,6 +1261,7 @@ class EngineStrutStyle implements ui.StrutStyle { _fontFamilyFallback, _fontSize, _height, + _textHeightBehavior, _leading, _fontWeight, _fontStyle, diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index 4659b903193ed..b31c1e600e4bd 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -192,16 +192,24 @@ enum TextDecorationStyle { wavy } +enum LeadingDistribution { + proportional, + even, +} + class TextHeightBehavior { const TextHeightBehavior({ this.applyHeightToFirstAscent = true, this.applyHeightToLastDescent = true, + this.leadingDistribution = LeadingDistribution.proportional, }); - const TextHeightBehavior.fromEncoded(int encoded) + TextHeightBehavior.fromEncoded(int encoded) : applyHeightToFirstAscent = (encoded & 0x1) == 0, - applyHeightToLastDescent = (encoded & 0x2) == 0; + applyHeightToLastDescent = (encoded & 0x2) == 0, + leadingDistribution = LeadingDistribution.values[encoded >> 2]; final bool applyHeightToFirstAscent; final bool applyHeightToLastDescent; + final LeadingDistribution leadingDistribution; int encode() { return (applyHeightToFirstAscent ? 0 : 1 << 0) | (applyHeightToLastDescent ? 0 : 1 << 1); } @@ -227,7 +235,8 @@ class TextHeightBehavior { String toString() { return 'TextHeightBehavior(' 'applyHeightToFirstAscent: $applyHeightToFirstAscent, ' - 'applyHeightToLastDescent: $applyHeightToLastDescent' + 'applyHeightToLastDescent: $applyHeightToLastDescent, ' + 'leadingDistribution: $leadingDistribution' ')'; } } @@ -359,6 +368,7 @@ abstract class StrutStyle { List? fontFamilyFallback, double? fontSize, double? height, + TextHeightBehavior? textHeightBehavior, double? leading, FontWeight? fontWeight, FontStyle? fontStyle, From d6c9f49f92b500aa394d14ca2be4b64ff7299b93 Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Wed, 3 Feb 2021 11:05:02 -0800 Subject: [PATCH 07/13] more tests --- testing/dart/text_test.dart | 10 ++++ third_party/txt/tests/paragraph_unittests.cc | 60 ++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/testing/dart/text_test.dart b/testing/dart/text_test.dart index 536bed84cf89e..13ebecd7b7465 100644 --- a/testing/dart/text_test.dart +++ b/testing/dart/text_test.dart @@ -54,6 +54,10 @@ void main() { const TextHeightBehavior behavior3 = TextHeightBehavior( applyHeightToLastDescent: false ); + const TextHeightBehavior behavior4 = TextHeightBehavior( + applyHeightToLastDescent: false, + leadingDistribution: LeadingDistribution.even, + ); test('default constructor works', () { expect(behavior0.applyHeightToFirstAscent, equals(true)); @@ -67,6 +71,9 @@ void main() { expect(behavior3.applyHeightToFirstAscent, equals(true)); expect(behavior3.applyHeightToLastDescent, equals(false)); + + expect(behavior4.applyHeightToLastDescent, equals(false)); + expect(behavior4.leadingDistribution, equals(LeadingDistribution.even)); }); test('encode works', () { @@ -74,6 +81,7 @@ void main() { expect(behavior1.encode(), equals(3)); expect(behavior2.encode(), equals(1)); expect(behavior3.encode(), equals(2)); + expect(behavior4.encode(), equals(6)); }); test('decode works', () { @@ -81,6 +89,7 @@ void main() { expect(TextHeightBehavior.fromEncoded(3), equals(behavior1)); expect(TextHeightBehavior.fromEncoded(1), equals(behavior2)); expect(TextHeightBehavior.fromEncoded(2), equals(behavior3)); + expect(TextHeightBehavior.fromEncoded(6), equals(behavior4)); }); test('toString works', () { @@ -88,6 +97,7 @@ void main() { expect(behavior1.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: false, leadingDistribution: LeadingDistribution.proportional)')); expect(behavior2.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: true, leadingDistribution: LeadingDistribution.proportional)')); expect(behavior3.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false, leadingDistribution: LeadingDistribution.proportional)')); + expect(behavior4.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false, leadingDistribution: LeadingDistribution.even)')); }); }); diff --git a/third_party/txt/tests/paragraph_unittests.cc b/third_party/txt/tests/paragraph_unittests.cc index 71973f0b4ee79..a109887aaa7a1 100644 --- a/third_party/txt/tests/paragraph_unittests.cc +++ b/third_party/txt/tests/paragraph_unittests.cc @@ -7045,4 +7045,64 @@ TEST_F(ParagraphTest, TextHeightBehaviorRectsParagraph) { ASSERT_TRUE(Snapshot()); } +TEST_F(ParagraphTest, MixedTextHeightBehaviorRectsParagraph) { + const char* text = "0123456789"; + 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.text_height_behavior will be overridden later. + paragraph_style.text_height_behavior = + txt::TextHeightBehavior::kDisableFirstAscent | + txt::TextHeightBehavior::kDisableLastDescent; + + txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.color = SK_ColorBLACK; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 30; + text_style.height = 5; + text_style.has_height_override = true; + text_style.has_text_height_behavior_override = true; + text_style.text_height_behavior = txt::TextHeightBehavior::kDisableAll | + txt::TextHeightBehavior::kHalfLeading; + builder.PushStyle(text_style); + builder.AddText(u16_text); + + text_style.text_height_behavior = txt::TextHeightBehavior::kHalfLeading; + builder.PushStyle(text_style); + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = BuildParagraph(builder); + paragraph->Layout(GetTestCanvasWidth() - 300); + + 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, 20, rect_height_style, rect_width_style); + + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + // The line-height is the same as not having the kDisableAll flag. + EXPECT_GT(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom() - boxes[0].rect.top(), 150.0); + + ASSERT_TRUE(Snapshot()); +} } // namespace txt From c31296840f2bef1ef6a638fae225bfca96b42fdb Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Fri, 5 Feb 2021 17:55:14 -0800 Subject: [PATCH 08/13] fix tests --- lib/ui/text.dart | 24 +++++++-------- third_party/txt/src/txt/paragraph_txt.cc | 39 +++++++++++++----------- third_party/txt/src/txt/text_style.cc | 3 +- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/lib/ui/text.dart b/lib/ui/text.dart index eb057c6a224a0..a7735223500f2 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -1993,25 +1993,25 @@ class TextStyle { 'decorationColor: ${ _encoded[0] & 0x00008 == 0x00008 ? Color(_encoded[3]) : "unspecified"}, ' 'decorationStyle: ${ _encoded[0] & 0x00010 == 0x00010 ? TextDecorationStyle.values[_encoded[4]] : "unspecified"}, ' // The decorationThickness is not in encoded order in order to keep it near the other decoration properties. - 'decorationThickness: ${_encoded[0] & 0x00100 == 0x00100 ? _decorationThickness : "unspecified"}, ' + 'decorationThickness: ${_encoded[0] & 0x00200 == 0x00200 ? _decorationThickness : "unspecified"}, ' 'fontWeight: ${ _encoded[0] & 0x00020 == 0x00020 ? FontWeight.values[_encoded[5]] : "unspecified"}, ' 'fontStyle: ${ _encoded[0] & 0x00040 == 0x00040 ? FontStyle.values[_encoded[6]] : "unspecified"}, ' 'textBaseline: ${ _encoded[0] & 0x00080 == 0x00080 ? TextBaseline.values[_encoded[7]] : "unspecified"}, ' - 'fontFamily: ${ _encoded[0] & 0x00200 == 0x00200 + 'fontFamily: ${ _encoded[0] & 0x00400 == 0x00400 && _fontFamily != '' ? _fontFamily : "unspecified"}, ' - 'fontFamilyFallback: ${ _encoded[0] & 0x00200 == 0x00200 + 'fontFamilyFallback: ${ _encoded[0] & 0x00400 == 0x00400 && _fontFamilyFallback != null && _fontFamilyFallback!.isNotEmpty ? _fontFamilyFallback : "unspecified"}, ' - 'fontSize: ${ _encoded[0] & 0x00400 == 0x00400 ? _fontSize : "unspecified"}, ' - 'letterSpacing: ${ _encoded[0] & 0x00800 == 0x00800 ? "${_letterSpacing}x" : "unspecified"}, ' - 'wordSpacing: ${ _encoded[0] & 0x01000 == 0x01000 ? "${_wordSpacing}x" : "unspecified"}, ' - 'height: ${ _encoded[0] & 0x02000 == 0x02000 ? "${_height}x" : "unspecified"}, ' + 'fontSize: ${ _encoded[0] & 0x00800 == 0x00800 ? _fontSize : "unspecified"}, ' + 'letterSpacing: ${ _encoded[0] & 0x01000 == 0x01000 ? "${_letterSpacing}x" : "unspecified"}, ' + 'wordSpacing: ${ _encoded[0] & 0x02000 == 0x02000 ? "${_wordSpacing}x" : "unspecified"}, ' + 'height: ${ _encoded[0] & 0x04000 == 0x04000 ? "${_height}x" : "unspecified"}, ' 'textHeightBehavior: ${ _encoded[0] & 0x0100 == 0x0100 ? "${TextHeightBehavior.fromEncoded(_encoded[8])}" : "unspecified"}, ' - 'locale: ${ _encoded[0] & 0x04000 == 0x04000 ? _locale : "unspecified"}, ' - 'background: ${ _encoded[0] & 0x08000 == 0x08000 ? _background : "unspecified"}, ' - 'foreground: ${ _encoded[0] & 0x10000 == 0x10000 ? _foreground : "unspecified"}, ' - 'shadows: ${ _encoded[0] & 0x20000 == 0x20000 ? _shadows : "unspecified"}, ' - 'fontFeatures: ${ _encoded[0] & 0x40000 == 0x40000 ? _fontFeatures : "unspecified"}' + 'locale: ${ _encoded[0] & 0x08000 == 0x08000 ? _locale : "unspecified"}, ' + 'background: ${ _encoded[0] & 0x10000 == 0x10000 ? _background : "unspecified"}, ' + 'foreground: ${ _encoded[0] & 0x20000 == 0x20000 ? _foreground : "unspecified"}, ' + 'shadows: ${ _encoded[0] & 0x40000 == 0x40000 ? _shadows : "unspecified"}, ' + 'fontFeatures: ${ _encoded[0] & 0x80000 == 0x80000 ? _fontFeatures : "unspecified"}' ')'; } } diff --git a/third_party/txt/src/txt/paragraph_txt.cc b/third_party/txt/src/txt/paragraph_txt.cc index 3e4bbc2213971..6dcc60f3ce590 100644 --- a/third_party/txt/src/txt/paragraph_txt.cc +++ b/third_party/txt/src/txt/paragraph_txt.cc @@ -1198,10 +1198,11 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics, size_t line_limit) { if (!strut_.force_strut) { const double metrics_font_height = metrics.fDescent - metrics.fAscent; - // The overall height of the glyph blob. If neither the ascent and the + // The overall height of the glyph blob. If neither the ascent or the // descent is disabled, we have block_height = ascent + descent, where // "ascent" is the extent from the top of the blob to its baseline, and - // "descent" is the extent from the text blob's baseline to its bottom. + // "descent" is the extent from the text blob's baseline to its bottom. Not + // to be mistaken with the font's ascent and descent. const double blob_height = style.has_height_override ? style.height * style.font_size : metrics_font_height + metrics.fLeading; @@ -1262,14 +1263,25 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics, const double font_height = shouldNormalizeFont ? style.font_size : metrics_font_height; - const double leading = - text_height_behavior & TextHeightBehavior::kHalfLeading - ? blob_height - font_height - : style.has_height_override ? 0.0 - : metrics.fLeading; - + // Reserve the outermost vertical space we want to distribute evenly over + // and under the text ("half-leading"). + double leading; + if (text_height_behavior & TextHeightBehavior::kHalfLeading) { + leading = blob_height - font_height; + } else { + leading = style.has_height_override ? 0.0 : metrics.fLeading; + } const double half_leading = leading / 2; + + // Proportionally distribute the remaining vertical space above and below + // the glyph blob's baseline, per the font's ascent/discent ratio. const double available_vspace = blob_height - leading; + const double modifiedAscent = + -metrics.fAscent / metrics_font_height * available_vspace + + half_leading; + const double modifiedDescent = + metrics.fDescent / metrics_font_height * available_vspace + + half_leading; const bool disableAscent = line_number == 0 && @@ -1278,17 +1290,8 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics, line_number == line_limit - 1 && text_height_behavior & TextHeightBehavior::kDisableLastDescent; - // Proportionally distribute the remaining vertical space above and below - // the glyph blob's baseline, per the font's ascent/discent ratio. - const double modifiedAscent = - -metrics.fAscent / metrics_font_height * available_vspace + - half_leading; double ascent = disableAscent ? -metrics.fAscent : modifiedAscent; - double descent = disableDescent ? metrics.fDescent - //: blob_height - leading - modifiedAscent; - : metrics.fDescent / metrics_font_height * - available_vspace + - half_leading; + double descent = disableDescent ? metrics.fDescent : modifiedDescent; ComputePlaceholder(placeholder_run, ascent, descent); diff --git a/third_party/txt/src/txt/text_style.cc b/third_party/txt/src/txt/text_style.cc index 1a279c55cf3ca..6f5be15ea7146 100644 --- a/third_party/txt/src/txt/text_style.cc +++ b/third_party/txt/src/txt/text_style.cc @@ -51,8 +51,7 @@ bool TextStyle::equals(const TextStyle& other) const { if (has_text_height_behavior_override != other.has_text_height_behavior_override) return false; - if (has_text_height_behavior_override && - text_height_behavior != other.text_height_behavior) + if (text_height_behavior != other.text_height_behavior) return false; if (locale != other.locale) return false; From 96ceadbffed95fc1fcbd4f5d4642932c04f57825 Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Mon, 8 Feb 2021 15:30:47 -0800 Subject: [PATCH 09/13] typo --- lib/ui/text/paragraph_builder.cc | 4 ++-- lib/web_ui/lib/src/engine/text/paragraph.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ui/text/paragraph_builder.cc b/lib/ui/text/paragraph_builder.cc index 70da9f48cca04..2a8fa7eef2c7b 100644 --- a/lib/ui/text/paragraph_builder.cc +++ b/lib/ui/text/paragraph_builder.cc @@ -129,7 +129,7 @@ const int sFontStyleMask = 1 << sFontStyleIndex; const int sFontFamilyMask = 1 << sFontFamilyIndex; const int sFontSizeMask = 1 << sFontSizeIndex; const int sHeightMask = 1 << sHeightIndex; -const int stextHeightBehaviorMask = 1 << sTextHeightBehaviorIndex; +const int sTextHeightBehaviorMask = 1 << sTextHeightBehaviorIndex; const int sLeadingMask = 1 << sLeadingIndex; const int sForceStrutHeightMask = 1 << sForceStrutHeightIndex; @@ -216,7 +216,7 @@ void decodeStrut(Dart_Handle strut_data, paragraph_style.strut_has_height_override = true; // TextHeightBehavior does not affect layout if height is not set. - if (mask & stextHeightBehaviorMask) { + if (mask & sTextHeightBehaviorMask) { paragraph_style.strut_text_height_behavior = uint8_data[byte_count]; } } diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 9fdcfb145a267..baade9e11cbfb 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -1211,7 +1211,7 @@ class EngineStrutStyle implements ui.StrutStyle { List? fontFamilyFallback, double? fontSize, double? height, - //TODO(LongCatIsLooong): implement textHeightBehavir. + //TODO(LongCatIsLooong): implement textHeightBehavior. ui.TextHeightBehavior? textHeightBehavior, double? leading, ui.FontWeight? fontWeight, From 80fd926fcfc1c990e7d89909d41b84087eb72750 Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Mon, 8 Feb 2021 15:52:17 -0800 Subject: [PATCH 10/13] FLT_* -> std::numeric_limits --- third_party/txt/src/txt/paragraph_txt.cc | 16 ++++++++-------- third_party/txt/src/txt/paragraph_txt.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/third_party/txt/src/txt/paragraph_txt.cc b/third_party/txt/src/txt/paragraph_txt.cc index 6dcc60f3ce590..98f9dbe2f1415 100644 --- a/third_party/txt/src/txt/paragraph_txt.cc +++ b/third_party/txt/src/txt/paragraph_txt.cc @@ -518,8 +518,8 @@ bool ParagraphTxt::IsStrutValid() const { } void ParagraphTxt::ComputeStrut(StrutMetrics* strut, SkFont& font) { - strut->ascent = -FLT_MAX; - strut->descent = -FLT_MAX; + strut->ascent = std::numeric_limits::lowest(); + strut->descent = std::numeric_limits::lowest(); strut->leading = 0; strut->half_leading = 0; strut->line_height = 0; @@ -682,8 +682,8 @@ void ParagraphTxt::Layout(double width) { glyph_lines_.clear(); code_unit_runs_.clear(); inline_placeholder_code_unit_runs_.clear(); - max_right_ = -FLT_MAX; - min_left_ = FLT_MAX; + max_right_ = std::numeric_limits::lowest(); + min_left_ = std::numeric_limits::max(); final_line_count_ = 0; if (!ComputeLineBreaks()) @@ -1675,8 +1675,8 @@ std::vector ParagraphTxt::GetRectsForRange( // Per-line metrics for max and min coordinates for left and right boxes. // These metrics cannot be calculated in layout generically because of // selections that do not cover the whole line. - SkScalar max_right = -FLT_MAX; - SkScalar min_left = FLT_MAX; + SkScalar max_right = std::numeric_limits::lowest(); + SkScalar min_left = std::numeric_limits::max(); }; std::map line_box_metrics; @@ -1953,8 +1953,8 @@ std::vector ParagraphTxt::GetRectsForPlaceholders() { // Per-line metrics for max and min coordinates for left and right boxes. // These metrics cannot be calculated in layout generically because of // selections that do not cover the whole line. - SkScalar max_right = -FLT_MAX; - SkScalar min_left = FLT_MAX; + SkScalar max_right = std::numeric_limits::lowest(); + SkScalar min_left = std::numeric_limits::max(); }; std::vector boxes; diff --git a/third_party/txt/src/txt/paragraph_txt.h b/third_party/txt/src/txt/paragraph_txt.h index 0919130868211..e0cf8a6ddd6d0 100644 --- a/third_party/txt/src/txt/paragraph_txt.h +++ b/third_party/txt/src/txt/paragraph_txt.h @@ -315,8 +315,8 @@ class ParagraphTxt : public Paragraph { double longest_line_ = -1.0f; double max_intrinsic_width_ = 0; double min_intrinsic_width_ = 0; - double alphabetic_baseline_ = FLT_MAX; - double ideographic_baseline_ = FLT_MAX; + double alphabetic_baseline_ = std::numeric_limits::max(); + double ideographic_baseline_ = std::numeric_limits::max(); bool needs_layout_ = true; From e396b6fe6065782d1c67f8cc0ee14a91cbe5b776 Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Thu, 18 Feb 2021 15:40:14 -0800 Subject: [PATCH 11/13] TextStyle.textHeightBehavior -> TextStyle.leadingDistribution --- lib/ui/text.dart | 76 ++++++++++--------- lib/ui/text/paragraph_builder.cc | 21 ++--- lib/web_ui/lib/src/engine/text/paragraph.dart | 10 +-- lib/web_ui/lib/src/ui/text.dart | 15 ++-- testing/dart/text_test.dart | 31 ++++++++ third_party/txt/src/txt/paragraph_style.h | 33 +++++++- third_party/txt/src/txt/paragraph_txt.cc | 35 +++++---- third_party/txt/src/txt/text_style.cc | 6 +- third_party/txt/src/txt/text_style.h | 34 +-------- third_party/txt/tests/paragraph_unittests.cc | 62 ++++++++------- 10 files changed, 184 insertions(+), 139 deletions(-) diff --git a/lib/ui/text.dart b/lib/ui/text.dart index a7735223500f2..b779f743f4d59 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -1748,7 +1748,7 @@ bool _listEquals(List? a, List? b) { // // - Element 7: The enum index of the |textBaseline|. // -// - Element 8: The encoded value of the |textHeightBehavior|. +// - Element 8: The encoded value of the |leadingDistribution|. // Int32List _encodeTextStyle( Color? color, @@ -1765,7 +1765,7 @@ Int32List _encodeTextStyle( double? letterSpacing, double? wordSpacing, double? height, - TextHeightBehavior? textHeightBehavior, + LeadingDistribution? leadingDistribution, Locale? locale, Paint? background, Paint? foreground, @@ -1801,9 +1801,9 @@ Int32List _encodeTextStyle( result[0] |= 1 << 7; result[7] = textBaseline.index; } - if (textHeightBehavior != null) { + if (leadingDistribution != null) { result[0] |= 1 << 8; - result[8] = textHeightBehavior.encode(); + result[8] = leadingDistribution.index; } if (decorationThickness != null) { result[0] |= 1 << 9; @@ -1883,7 +1883,7 @@ class TextStyle { /// * `textBaseline`: The common baseline that should be aligned between this text span and its parent text span, or, for the root text spans, with the line box. /// * `height`: The height of this text span, as a multiplier of the font size. Omitting `height` will allow the line height /// to take the height as defined by the font, which may not be exactly the height of the fontSize. - /// * `textHeightBehavior`: When `height` is specified, how the extra vertical space should be distributed over and under the text. + /// * `leadingDistribution`: When `height` is specified, how the extra vertical space should be distributed over and under the text. /// * `locale`: The locale used to select region-specific glyphs. /// * `background`: The paint drawn as a background for the text. /// * `foreground`: The paint used to draw the text. If this is specified, `color` must be null. @@ -1903,7 +1903,7 @@ class TextStyle { double? letterSpacing, double? wordSpacing, double? height, - TextHeightBehavior? textHeightBehavior, + LeadingDistribution? leadingDistribution, Locale? locale, Paint? background, Paint? foreground, @@ -1928,7 +1928,7 @@ class TextStyle { letterSpacing, wordSpacing, height, - textHeightBehavior, + leadingDistribution, locale, background, foreground, @@ -1988,30 +1988,30 @@ class TextStyle { @override String toString() { return 'TextStyle(' - 'color: ${ _encoded[0] & 0x00002 == 0x00002 ? Color(_encoded[1]) : "unspecified"}, ' - 'decoration: ${ _encoded[0] & 0x00004 == 0x00004 ? TextDecoration._(_encoded[2]) : "unspecified"}, ' - 'decorationColor: ${ _encoded[0] & 0x00008 == 0x00008 ? Color(_encoded[3]) : "unspecified"}, ' - 'decorationStyle: ${ _encoded[0] & 0x00010 == 0x00010 ? TextDecorationStyle.values[_encoded[4]] : "unspecified"}, ' + 'color: ${ _encoded[0] & 0x00002 == 0x00002 ? Color(_encoded[1]) : "unspecified"}, ' + 'decoration: ${ _encoded[0] & 0x00004 == 0x00004 ? TextDecoration._(_encoded[2]) : "unspecified"}, ' + 'decorationColor: ${ _encoded[0] & 0x00008 == 0x00008 ? Color(_encoded[3]) : "unspecified"}, ' + 'decorationStyle: ${ _encoded[0] & 0x00010 == 0x00010 ? TextDecorationStyle.values[_encoded[4]] : "unspecified"}, ' // The decorationThickness is not in encoded order in order to keep it near the other decoration properties. - 'decorationThickness: ${_encoded[0] & 0x00200 == 0x00200 ? _decorationThickness : "unspecified"}, ' - 'fontWeight: ${ _encoded[0] & 0x00020 == 0x00020 ? FontWeight.values[_encoded[5]] : "unspecified"}, ' - 'fontStyle: ${ _encoded[0] & 0x00040 == 0x00040 ? FontStyle.values[_encoded[6]] : "unspecified"}, ' - 'textBaseline: ${ _encoded[0] & 0x00080 == 0x00080 ? TextBaseline.values[_encoded[7]] : "unspecified"}, ' + 'decorationThickness: ${_encoded[0] & 0x00200 == 0x00200 ? _decorationThickness : "unspecified"}, ' + 'fontWeight: ${ _encoded[0] & 0x00020 == 0x00020 ? FontWeight.values[_encoded[5]] : "unspecified"}, ' + 'fontStyle: ${ _encoded[0] & 0x00040 == 0x00040 ? FontStyle.values[_encoded[6]] : "unspecified"}, ' + 'textBaseline: ${ _encoded[0] & 0x00080 == 0x00080 ? TextBaseline.values[_encoded[7]] : "unspecified"}, ' 'fontFamily: ${ _encoded[0] & 0x00400 == 0x00400 - && _fontFamily != '' ? _fontFamily : "unspecified"}, ' + && _fontFamily != '' ? _fontFamily : "unspecified"}, ' 'fontFamilyFallback: ${ _encoded[0] & 0x00400 == 0x00400 && _fontFamilyFallback != null - && _fontFamilyFallback!.isNotEmpty ? _fontFamilyFallback : "unspecified"}, ' - 'fontSize: ${ _encoded[0] & 0x00800 == 0x00800 ? _fontSize : "unspecified"}, ' - 'letterSpacing: ${ _encoded[0] & 0x01000 == 0x01000 ? "${_letterSpacing}x" : "unspecified"}, ' - 'wordSpacing: ${ _encoded[0] & 0x02000 == 0x02000 ? "${_wordSpacing}x" : "unspecified"}, ' - 'height: ${ _encoded[0] & 0x04000 == 0x04000 ? "${_height}x" : "unspecified"}, ' - 'textHeightBehavior: ${ _encoded[0] & 0x0100 == 0x0100 ? "${TextHeightBehavior.fromEncoded(_encoded[8])}" : "unspecified"}, ' - 'locale: ${ _encoded[0] & 0x08000 == 0x08000 ? _locale : "unspecified"}, ' - 'background: ${ _encoded[0] & 0x10000 == 0x10000 ? _background : "unspecified"}, ' - 'foreground: ${ _encoded[0] & 0x20000 == 0x20000 ? _foreground : "unspecified"}, ' - 'shadows: ${ _encoded[0] & 0x40000 == 0x40000 ? _shadows : "unspecified"}, ' - 'fontFeatures: ${ _encoded[0] & 0x80000 == 0x80000 ? _fontFeatures : "unspecified"}' + && _fontFamilyFallback!.isNotEmpty ? _fontFamilyFallback : "unspecified"}, ' + 'fontSize: ${ _encoded[0] & 0x00800 == 0x00800 ? _fontSize : "unspecified"}, ' + 'letterSpacing: ${ _encoded[0] & 0x01000 == 0x01000 ? "${_letterSpacing}x" : "unspecified"}, ' + 'wordSpacing: ${ _encoded[0] & 0x02000 == 0x02000 ? "${_wordSpacing}x" : "unspecified"}, ' + 'height: ${ _encoded[0] & 0x04000 == 0x04000 ? "${_height}x" : "unspecified"}, ' + 'leadingDistribution: ${_encoded[0] & 0x0100 == 0x0100 ? "${LeadingDistribution.values[_encoded[8]]}" : "unspecified"}, ' + 'locale: ${ _encoded[0] & 0x08000 == 0x08000 ? _locale : "unspecified"}, ' + 'background: ${ _encoded[0] & 0x10000 == 0x10000 ? _background : "unspecified"}, ' + 'foreground: ${ _encoded[0] & 0x20000 == 0x20000 ? _foreground : "unspecified"}, ' + 'shadows: ${ _encoded[0] & 0x40000 == 0x40000 ? _shadows : "unspecified"}, ' + 'fontFeatures: ${ _encoded[0] & 0x80000 == 0x80000 ? _fontFeatures : "unspecified"}' ')'; } } @@ -2139,7 +2139,10 @@ class ParagraphStyle { /// be exactly the height of the `fontSize`. /// /// * `textHeightBehavior`: Specifies how the `height` multiplier is - /// applied to ascent and the descent of the text. + /// applied to ascent of the first line and the descent of the last line. + /// + /// * `leadingDistribution`: Specifies how the extra vertical space added by + /// the `height` multiplier should be distributed over and under the text. /// /// * `fontWeight`: The typeface thickness to use when painting the text /// (e.g., bold). @@ -2256,7 +2259,7 @@ ByteData _encodeStrut( List? fontFamilyFallback, double? fontSize, double? height, - TextHeightBehavior? textHeightBehavior, + LeadingDistribution? leadingDistribution, double? leading, FontWeight? fontWeight, FontStyle? fontStyle, @@ -2264,7 +2267,7 @@ ByteData _encodeStrut( if (fontFamily == null && fontSize == null && height == null && - textHeightBehavior == null && + leadingDistribution == null && leading == null && fontWeight == null && fontStyle == null && @@ -2297,9 +2300,9 @@ ByteData _encodeStrut( bitmask |= 1 << 4; data.setFloat32(byteCount, height, _kFakeHostEndian); byteCount += 4; - if (textHeightBehavior != null) { + if (leadingDistribution != null) { bitmask |= 1 << 5; - data.setInt8(byteCount, textHeightBehavior.encode()); + data.setInt8(byteCount, leadingDistribution.index); byteCount += 4; } } @@ -2349,6 +2352,11 @@ class StrutStyle { /// * `leading`: The minimum amount of leading between lines as a multiple of /// the font size. `fontSize` must be provided for this property to take effect. /// + /// * `leadingDistribution`: Specifies how the extra vertical space added by + /// the `height` multiplier should be distributed over and under the text, + /// independent of `leading` (which is always distributed evenly over and + /// under text). + /// /// * `fontWeight`: The typeface thickness to use when painting the text /// (e.g., bold). /// @@ -2367,7 +2375,7 @@ class StrutStyle { List? fontFamilyFallback, double? fontSize, double? height, - TextHeightBehavior? textHeightBehavior, + LeadingDistribution? leadingDistribution, double? leading, FontWeight? fontWeight, FontStyle? fontStyle, @@ -2377,7 +2385,7 @@ class StrutStyle { fontFamilyFallback, fontSize, height, - textHeightBehavior, + leadingDistribution, leading, fontWeight, fontStyle, diff --git a/lib/ui/text/paragraph_builder.cc b/lib/ui/text/paragraph_builder.cc index 2a8fa7eef2c7b..d53ad7821044f 100644 --- a/lib/ui/text/paragraph_builder.cc +++ b/lib/ui/text/paragraph_builder.cc @@ -39,7 +39,7 @@ const int tsTextDecorationStyleIndex = 4; const int tsFontWeightIndex = 5; const int tsFontStyleIndex = 6; const int tsTextBaselineIndex = 7; -const int tsTextHeightBehaviorIndex = 8; +const int tsLeadingDistributionIndex = 8; const int tsTextDecorationThicknessIndex = 9; const int tsFontFamilyIndex = 10; const int tsFontSizeIndex = 11; @@ -60,7 +60,7 @@ const int tsTextDecorationThicknessMask = 1 << tsTextDecorationThicknessIndex; const int tsFontWeightMask = 1 << tsFontWeightIndex; const int tsFontStyleMask = 1 << tsFontStyleIndex; const int tsTextBaselineMask = 1 << tsTextBaselineIndex; -const int tsTextHeightBehaviorMask = 1 << tsTextHeightBehaviorIndex; +const int tsLeadingDistributionMask = 1 << tsLeadingDistributionIndex; const int tsFontFamilyMask = 1 << tsFontFamilyIndex; const int tsFontSizeMask = 1 << tsFontSizeIndex; const int tsLetterSpacingMask = 1 << tsLetterSpacingIndex; @@ -120,7 +120,7 @@ const int sFontStyleIndex = 1; const int sFontFamilyIndex = 2; const int sFontSizeIndex = 3; const int sHeightIndex = 4; -const int sTextHeightBehaviorIndex = 5; +const int sLeadingDistributionIndex = 5; const int sLeadingIndex = 6; const int sForceStrutHeightIndex = 7; @@ -129,7 +129,7 @@ const int sFontStyleMask = 1 << sFontStyleIndex; const int sFontFamilyMask = 1 << sFontFamilyIndex; const int sFontSizeMask = 1 << sFontSizeIndex; const int sHeightMask = 1 << sHeightIndex; -const int sTextHeightBehaviorMask = 1 << sTextHeightBehaviorIndex; +const int sLeadingDistributionMask = 1 << sLeadingDistributionIndex; const int sLeadingMask = 1 << sLeadingIndex; const int sForceStrutHeightMask = 1 << sForceStrutHeightIndex; @@ -215,9 +215,10 @@ void decodeStrut(Dart_Handle strut_data, paragraph_style.strut_height = float_data[float_count++]; paragraph_style.strut_has_height_override = true; - // TextHeightBehavior does not affect layout if height is not set. - if (mask & sTextHeightBehaviorMask) { - paragraph_style.strut_text_height_behavior = uint8_data[byte_count]; + // LeadingDistribution does not affect layout if height is not set. + if (mask & sLeadingDistributionMask) { + paragraph_style.strut_half_leading = uint8_data[byte_count]; + paragraph_style.strut_has_leading_distribution_override = true; } } if (mask & sLeadingMask) { @@ -419,9 +420,9 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, // property wasn't wired up either. } - style.has_text_height_behavior_override = mask & tsTextHeightBehaviorMask; - if (mask & tsTextHeightBehaviorMask) { - style.text_height_behavior = encoded[tsTextHeightBehaviorIndex]; + style.has_leading_distribution_override = mask & tsLeadingDistributionMask; + if (mask & tsLeadingDistributionMask) { + style.half_leading = encoded[tsLeadingDistributionIndex]; } if (mask & (tsFontWeightMask | tsFontStyleMask | tsFontSizeMask | diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index baade9e11cbfb..16a384d0ef2a8 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -1211,8 +1211,8 @@ class EngineStrutStyle implements ui.StrutStyle { List? fontFamilyFallback, double? fontSize, double? height, - //TODO(LongCatIsLooong): implement textHeightBehavior. - ui.TextHeightBehavior? textHeightBehavior, + //TODO(LongCatIsLooong): implement leadingDistribution. + ui.LeadingDistribution? leadingDistribution, double? leading, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, @@ -1221,7 +1221,7 @@ class EngineStrutStyle implements ui.StrutStyle { _fontFamilyFallback = fontFamilyFallback, _fontSize = fontSize, _height = height, - _textHeightBehavior = textHeightBehavior, + _leadingDistribution = leadingDistribution, _leading = leading, _fontWeight = fontWeight, _fontStyle = fontStyle, @@ -1235,7 +1235,7 @@ class EngineStrutStyle implements ui.StrutStyle { final ui.FontWeight? _fontWeight; final ui.FontStyle? _fontStyle; final bool? _forceStrutHeight; - final ui.TextHeightBehavior? _textHeightBehavior; + final ui.LeadingDistribution? _leadingDistribution; @override bool operator ==(Object other) { @@ -1249,7 +1249,6 @@ class EngineStrutStyle implements ui.StrutStyle { && other._fontFamily == _fontFamily && other._fontSize == _fontSize && other._height == _height - && other._textHeightBehavior == _textHeightBehavior && other._leading == _leading && other._fontWeight == _fontWeight && other._fontStyle == _fontStyle @@ -1263,7 +1262,6 @@ class EngineStrutStyle implements ui.StrutStyle { _fontFamilyFallback, _fontSize, _height, - _textHeightBehavior, _leading, _fontWeight, _fontStyle, diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index 6dcb126e46e2a..05e3bef57eefd 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -213,15 +213,13 @@ class TextHeightBehavior { const TextHeightBehavior({ this.applyHeightToFirstAscent = true, this.applyHeightToLastDescent = true, - this.leadingDistribution = LeadingDistribution.proportional, }); - TextHeightBehavior.fromEncoded(int encoded) + const TextHeightBehavior.fromEncoded(int encoded) : applyHeightToFirstAscent = (encoded & 0x1) == 0, - applyHeightToLastDescent = (encoded & 0x2) == 0, - leadingDistribution = LeadingDistribution.values[encoded >> 2]; + applyHeightToLastDescent = (encoded & 0x2) == 0; final bool applyHeightToFirstAscent; final bool applyHeightToLastDescent; - final LeadingDistribution leadingDistribution; + int encode() { return (applyHeightToFirstAscent ? 0 : 1 << 0) | (applyHeightToLastDescent ? 0 : 1 << 1); } @@ -247,8 +245,7 @@ class TextHeightBehavior { String toString() { return 'TextHeightBehavior(' 'applyHeightToFirstAscent: $applyHeightToFirstAscent, ' - 'applyHeightToLastDescent: $applyHeightToLastDescent, ' - 'leadingDistribution: $leadingDistribution' + 'applyHeightToLastDescent: $applyHeightToLastDescent' ')'; } } @@ -269,7 +266,7 @@ abstract class TextStyle { double? letterSpacing, double? wordSpacing, double? height, - TextHeightBehavior? textHeightBehavior, + LeadingDistribution? leadingDistribution, Locale? locale, Paint? background, Paint? foreground, @@ -380,7 +377,7 @@ abstract class StrutStyle { List? fontFamilyFallback, double? fontSize, double? height, - TextHeightBehavior? textHeightBehavior, + LeadingDistribution? leadingDistribution, double? leading, FontWeight? fontWeight, FontStyle? fontStyle, diff --git a/testing/dart/text_test.dart b/testing/dart/text_test.dart index 13ebecd7b7465..ed685fe59a34b 100644 --- a/testing/dart/text_test.dart +++ b/testing/dart/text_test.dart @@ -42,6 +42,37 @@ void main() { }); }); + group('TextStyle', () { + final TextStyle ts0 = TextStyle(fontWeight: FontWeight.w700, fontSize: 12.0, height: 123.0); + final TextStyle ts1 = TextStyle(color: const Color(0xFF00FF00), fontWeight: FontWeight.w800, fontSize: 10.0, height: 100.0); + final TextStyle ts2 = TextStyle(fontFamily: 'test'); + final TextStyle ts3 = TextStyle(fontFamily: 'foo', fontFamilyFallback: ['Roboto', 'test']); + final TextStyle ts4 = TextStyle(leadingDistribution: LeadingDistribution.even); + + test('toString works', () { + expect( + ts0.toString(), + equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 12.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 123.0x, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified)'), + ); + expect( + ts1.toString(), + equals('TextStyle(color: Color(0xff00ff00), decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 10.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 100.0x, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified)'), + ); + expect( + ts2.toString(), + equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: test, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified)'), + ); + expect( + ts3.toString(), + equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: foo, fontFamilyFallback: [Roboto, test], fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified)'), + ); + expect( + ts4.toString(), + equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, leadingDistribution: LeadingDistribution.even, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified)'), + ); + }); + }); + group('TextHeightBehavior', () { const TextHeightBehavior behavior0 = TextHeightBehavior(); const TextHeightBehavior behavior1 = TextHeightBehavior( diff --git a/third_party/txt/src/txt/paragraph_style.h b/third_party/txt/src/txt/paragraph_style.h index ce86ba2467174..d60d2638bf055 100644 --- a/third_party/txt/src/txt/paragraph_style.h +++ b/third_party/txt/src/txt/paragraph_style.h @@ -41,6 +41,34 @@ enum class TextDirection { ltr, }; +// Adjusts the leading over and under text. +// +// kDisableFirstAscent and kDisableLastDescent allow disabling height +// adjustments to first line's ascent and the last line's descent. If disabled, +// the line will use the default font metric provided ascent/descent and +// ParagraphStyle.height or TextStyle.height will not take effect. +// +// kHalfLeading determines how the leading is distributed over and under the +// text. When true, half of the leading is added to the top of the text and the +// other half is added to the bottom of the text. Otherwise, instead of +// distributing the space evenly, it's distributed proportionally to the font's +// ascent/descent ratio. +// +// The default behavior is kAll where height adjustments are enabled for all +// lines. +// +// Multiple behaviors can be applied at once with a bitwise | operator. For +// example, disabling first ascent and last descent can achieved with: +// +// (kDisableFirstAscent | kDisableLastDescent). +enum TextHeightBehavior { + kAll = 0x0, + kDisableFirstAscent = 0x1, + kDisableLastDescent = 0x2, + kDisableAll = 0x1 | 0x2, + kHalfLeading = 0x1 << 2, +}; + class ParagraphStyle { public: // Default TextStyle. Used in GetTextStyle() to obtain the base TextStyle to @@ -50,8 +78,8 @@ class ParagraphStyle { std::string font_family = ""; double font_size = 14; double height = 1; - size_t text_height_behavior = TextHeightBehavior::kAll; bool has_height_override = false; + size_t text_height_behavior = TextHeightBehavior::kAll; // Strut properties. strut_enabled must be set to true for the rest of the // properties to take effect. @@ -63,7 +91,8 @@ class ParagraphStyle { double strut_font_size = 14; double strut_height = 1; bool strut_has_height_override = false; - size_t strut_text_height_behavior = TextHeightBehavior::kAll; + bool strut_half_leading = false; + bool strut_has_leading_distribution_override = false; double strut_leading = -1; // Negative to use font's default leading. [0,inf) // to use custom leading as a ratio of font size. bool force_strut_height = false; diff --git a/third_party/txt/src/txt/paragraph_txt.cc b/third_party/txt/src/txt/paragraph_txt.cc index 685172b6e6d37..b14a94f962795 100644 --- a/third_party/txt/src/txt/paragraph_txt.cc +++ b/third_party/txt/src/txt/paragraph_txt.cc @@ -566,11 +566,14 @@ void ParagraphTxt::ComputeStrut(StrutMetrics* strut, SkFont& font) { : (paragraph_style_.strut_leading * paragraph_style_.strut_font_size); + const bool half_leading_enabled = + paragraph_style_.strut_has_leading_distribution_override + ? paragraph_style_.strut_half_leading + : paragraph_style_.text_height_behavior & + TextHeightBehavior::kHalfLeading; + const double available_height = - paragraph_style_.strut_text_height_behavior & - TextHeightBehavior::kHalfLeading - ? metrics_height - : strut_height; + half_leading_enabled ? metrics_height : strut_height; strut->ascent = (-strut_metrics.fAscent / metrics_height) * available_height; @@ -1208,10 +1211,11 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics, const double blob_height = style.has_height_override ? style.height * style.font_size : metrics_font_height + metrics.fLeading; - const size_t text_height_behavior = - style.has_text_height_behavior_override - ? style.text_height_behavior - : paragraph_style_.text_height_behavior; + const bool half_leading_enabled = + style.has_leading_distribution_override + ? style.half_leading + : paragraph_style_.text_height_behavior & + TextHeightBehavior::kHalfLeading; // Scale the ascent and descent such that the sum of ascent and // descent is `style.height * style.font_size`. @@ -1260,15 +1264,14 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics, // a sane, consistent, and reasonable "blob_height" to be specified, // though it breaks with what is done by any of the platforms above. const bool shouldNormalizeFont = - style.has_height_override && - !(text_height_behavior & TextHeightBehavior::kHalfLeading); + style.has_height_override && !half_leading_enabled; const double font_height = shouldNormalizeFont ? style.font_size : metrics_font_height; // Reserve the outermost vertical space we want to distribute evenly over // and under the text ("half-leading"). double leading; - if (text_height_behavior & TextHeightBehavior::kHalfLeading) { + if (half_leading_enabled) { leading = blob_height - font_height; } else { leading = style.has_height_override ? 0.0 : metrics.fLeading; @@ -1286,11 +1289,11 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics, half_leading; const bool disableAscent = - line_number == 0 && - text_height_behavior & TextHeightBehavior::kDisableFirstAscent; - const bool disableDescent = - line_number == line_limit - 1 && - text_height_behavior & TextHeightBehavior::kDisableLastDescent; + line_number == 0 && paragraph_style_.text_height_behavior & + TextHeightBehavior::kDisableFirstAscent; + const bool disableDescent = line_number == line_limit - 1 && + paragraph_style_.text_height_behavior & + TextHeightBehavior::kDisableLastDescent; double ascent = disableAscent ? -metrics.fAscent : modifiedAscent; double descent = disableDescent ? metrics.fDescent : modifiedDescent; diff --git a/third_party/txt/src/txt/text_style.cc b/third_party/txt/src/txt/text_style.cc index 6f5be15ea7146..b520c0f0dfa2f 100644 --- a/third_party/txt/src/txt/text_style.cc +++ b/third_party/txt/src/txt/text_style.cc @@ -48,10 +48,10 @@ bool TextStyle::equals(const TextStyle& other) const { return false; if (has_height_override != other.has_height_override) return false; - if (has_text_height_behavior_override != - other.has_text_height_behavior_override) + if (has_leading_distribution_override != + other.has_leading_distribution_override) return false; - if (text_height_behavior != other.text_height_behavior) + if (half_leading != other.half_leading) return false; if (locale != other.locale) return false; diff --git a/third_party/txt/src/txt/text_style.h b/third_party/txt/src/txt/text_style.h index dc818ef3d7b83..6d6bd34766343 100644 --- a/third_party/txt/src/txt/text_style.h +++ b/third_party/txt/src/txt/text_style.h @@ -31,34 +31,6 @@ namespace txt { -// Adjusts the leading over and under text. -// -// kDisableFirstAscent and kDisableLastDescent allow disabling height -// adjustments to first line's ascent and the last line's descent. If disabled, -// the line will use the default font metric provided ascent/descent and -// ParagraphStyle.height or TextStyle.height will not take effect. -// -// kHalfLeading determines how the leading is distributed over and under the -// text. When true, half of the leading is added to the top of the text and the -// other half is added to the bottom of the text. Otherwise, instead of -// distributing the space evenly, it's distributed proportionally to the font's -// ascent/descent ratio. -// -// The default behavior is kAll where height adjustments are enabled for all -// lines. -// -// Multiple behaviors can be applied at once with a bitwise | operator. For -// example, disabling first ascent and last descent can achieved with: -// -// (kDisableFirstAscent | kDisableLastDescent). -enum TextHeightBehavior { - kAll = 0x0, - kDisableFirstAscent = 0x1, - kDisableLastDescent = 0x2, - kDisableAll = 0x1 | 0x2, - kHalfLeading = 0x1 << 2, -}; - class TextStyle { public: SkColor color = SK_ColorWHITE; @@ -72,8 +44,10 @@ class TextStyle { FontWeight font_weight = FontWeight::w400; FontStyle font_style = FontStyle::normal; TextBaseline text_baseline = TextBaseline::kAlphabetic; - size_t text_height_behavior = TextHeightBehavior::kAll; - bool has_text_height_behavior_override = false; + bool half_leading = false; + // whether TextStyle.half_leading should be applied or we should defer to + // ParagraphStyle.half_leading. + bool has_leading_distribution_override = false; // An ordered list of fonts in order of priority. The first font is more // highly preferred than the last font. std::vector font_families; diff --git a/third_party/txt/tests/paragraph_unittests.cc b/third_party/txt/tests/paragraph_unittests.cc index 900f908ab88a2..fa9d0500138ef 100644 --- a/third_party/txt/tests/paragraph_unittests.cc +++ b/third_party/txt/tests/paragraph_unittests.cc @@ -1903,10 +1903,9 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(HeightOverrideHalfLeadingParagraph)) { text_style.color = SK_ColorBLACK; text_style.height = 3.6345; text_style.has_height_override = true; - // Disables text height behavior override so it should defaults to the - // paragraph style. - text_style.has_text_height_behavior_override = false; - text_style.text_height_behavior = TextHeightBehavior::kAll; + // Disables text style leading distribution behavior override so it defaults + // to the paragraph style. + text_style.has_leading_distribution_override = false; builder.PushStyle(text_style); builder.AddText(u16_text); @@ -1979,8 +1978,8 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(HeightOverrideHalfLeadingTextStyle)) { text_style.height = 3.6345; text_style.has_height_override = true; // Override paragraph_style.text_height_behavior: - text_style.has_text_height_behavior_override = true; - text_style.text_height_behavior = TextHeightBehavior::kHalfLeading; + text_style.has_leading_distribution_override = true; + text_style.half_leading = true; builder.PushStyle(text_style); builder.AddText(u16_text); @@ -2061,14 +2060,14 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(MixedTextHeightBehaviorSameLine)) { text_style.height = 3.6345; text_style.has_height_override = true; // First run, with half-leading. - text_style.has_text_height_behavior_override = true; - text_style.text_height_behavior = TextHeightBehavior::kHalfLeading; + text_style.has_leading_distribution_override = true; + text_style.half_leading = true; builder.PushStyle(text_style); builder.AddText(u16_text); // Second run with AD-scaling. - text_style.has_text_height_behavior_override = true; - text_style.text_height_behavior = TextHeightBehavior::kAll; + text_style.has_leading_distribution_override = true; + text_style.half_leading = false; builder.PushStyle(text_style); builder.AddText(u16_text2); @@ -2145,14 +2144,14 @@ TEST_F(ParagraphTest, text_style.height = 0; text_style.has_height_override = true; // First run, with half-leading. - text_style.has_text_height_behavior_override = true; - text_style.text_height_behavior = TextHeightBehavior::kHalfLeading; + text_style.has_leading_distribution_override = true; + text_style.half_leading = true; builder.PushStyle(text_style); builder.AddText(u16_text); // Second run with AD-scaling. - text_style.has_text_height_behavior_override = true; - text_style.text_height_behavior = TextHeightBehavior::kAll; + text_style.has_leading_distribution_override = true; + text_style.half_leading = false; builder.PushStyle(text_style); builder.AddText(u16_text); @@ -2215,7 +2214,8 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(HeightOverrideHalfLeadingStrut)) { paragraph_style.strut_height = 3.6345; paragraph_style.strut_font_size = 20; paragraph_style.strut_font_families.push_back("Roboto"); - paragraph_style.strut_text_height_behavior = TextHeightBehavior::kHalfLeading; + paragraph_style.strut_has_leading_distribution_override = true; + paragraph_style.strut_half_leading = true; txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; @@ -2227,8 +2227,8 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(HeightOverrideHalfLeadingStrut)) { text_style.height = 3.6345; text_style.has_height_override = true; // Override paragraph_style.text_height_behavior: - text_style.has_text_height_behavior_override = true; - text_style.text_height_behavior = TextHeightBehavior::kHalfLeading; + text_style.has_leading_distribution_override = true; + text_style.half_leading = true; builder.PushStyle(text_style); builder.AddText(u16_text); @@ -2301,7 +2301,7 @@ TEST_F(ParagraphTest, paragraph_style.force_strut_height = true; paragraph_style.strut_font_size = 20; paragraph_style.strut_font_families.push_back("Roboto"); - paragraph_style.strut_text_height_behavior = TextHeightBehavior::kHalfLeading; + paragraph_style.strut_half_leading = true; txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; @@ -2314,14 +2314,14 @@ TEST_F(ParagraphTest, text_style.has_height_override = true; // First run, with half-leading. - text_style.has_text_height_behavior_override = true; - text_style.text_height_behavior = TextHeightBehavior::kHalfLeading; + text_style.has_leading_distribution_override = true; + text_style.half_leading = true; builder.PushStyle(text_style); builder.AddText(u16_text); // Second run with AD-scaling. - text_style.has_text_height_behavior_override = true; - text_style.text_height_behavior = TextHeightBehavior::kAll; + text_style.has_leading_distribution_override = true; + text_style.half_leading = false; builder.PushStyle(text_style); builder.AddText(u16_text); @@ -7101,7 +7101,7 @@ TEST_F(ParagraphTest, MixedTextHeightBehaviorRectsParagraph) { icu_text.getBuffer() + icu_text.length()); txt::ParagraphStyle paragraph_style; - // paragraph_style.text_height_behavior will be overridden later. + // The paragraph's first line and the last line use the font's ascent/descent. paragraph_style.text_height_behavior = txt::TextHeightBehavior::kDisableFirstAscent | txt::TextHeightBehavior::kDisableLastDescent; @@ -7114,16 +7114,17 @@ TEST_F(ParagraphTest, MixedTextHeightBehaviorRectsParagraph) { text_style.font_size = 30; text_style.height = 5; text_style.has_height_override = true; - text_style.has_text_height_behavior_override = true; - text_style.text_height_behavior = txt::TextHeightBehavior::kDisableAll | - txt::TextHeightBehavior::kHalfLeading; + text_style.has_leading_distribution_override = true; + text_style.half_leading = true; + builder.PushStyle(text_style); builder.AddText(u16_text); - text_style.text_height_behavior = txt::TextHeightBehavior::kHalfLeading; + text_style.half_leading = false; builder.PushStyle(text_style); builder.AddText(u16_text); + // 2 identical runs except the first run has half-leading enabled. builder.Pop(); auto paragraph = BuildParagraph(builder); @@ -7148,9 +7149,12 @@ TEST_F(ParagraphTest, MixedTextHeightBehaviorRectsParagraph) { for (size_t i = 0; i < boxes.size(); ++i) { GetCanvas()->drawRect(boxes[i].rect, paint); } - // The line-height is the same as not having the kDisableAll flag. + // The kDisableAll flag is applied. EXPECT_GT(boxes.size(), 1ull); - EXPECT_FLOAT_EQ(boxes[0].rect.bottom() - boxes[0].rect.top(), 150.0); + // The height of the line equals to the metrics height of the font + // (ascent + descent). + EXPECT_FLOAT_EQ(boxes[0].rect.bottom() - boxes[0].rect.top(), + 27.8320312 + 7.32421875); ASSERT_TRUE(Snapshot()); } From 45964a12eb256aaf0ddb0b4829baf0aac2afb1aa Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Thu, 18 Feb 2021 16:42:16 -0800 Subject: [PATCH 12/13] add missing TextHeightBeahvior field --- lib/web_ui/lib/src/engine/text/paragraph.dart | 2 ++ lib/web_ui/lib/src/ui/text.dart | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 16a384d0ef2a8..6afc59589d25f 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -1250,6 +1250,7 @@ class EngineStrutStyle implements ui.StrutStyle { && other._fontSize == _fontSize && other._height == _height && other._leading == _leading + && other._leadingDistribution == _leadingDistribution && other._fontWeight == _fontWeight && other._fontStyle == _fontStyle && other._forceStrutHeight == _forceStrutHeight @@ -1263,6 +1264,7 @@ class EngineStrutStyle implements ui.StrutStyle { _fontSize, _height, _leading, + _leadingDistribution, _fontWeight, _fontStyle, _forceStrutHeight, diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index 05e3bef57eefd..64de60dd71ae4 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -213,15 +213,20 @@ class TextHeightBehavior { const TextHeightBehavior({ this.applyHeightToFirstAscent = true, this.applyHeightToLastDescent = true, + this.leadingDistribution = LeadingDistribution.proportional, }); - const TextHeightBehavior.fromEncoded(int encoded) - : applyHeightToFirstAscent = (encoded & 0x1) == 0, - applyHeightToLastDescent = (encoded & 0x2) == 0; + TextHeightBehavior.fromEncoded(int encoded) + : applyHeightToFirstAscent = (encoded & 0x1) == 0, + applyHeightToLastDescent = (encoded & 0x2) == 0, + leadingDistribution = LeadingDistribution.values[encoded >> 2]; final bool applyHeightToFirstAscent; final bool applyHeightToLastDescent; + final LeadingDistribution leadingDistribution; int encode() { - return (applyHeightToFirstAscent ? 0 : 1 << 0) | (applyHeightToLastDescent ? 0 : 1 << 1); + return (applyHeightToFirstAscent ? 0 : 1 << 0) + | (applyHeightToLastDescent ? 0 : 1 << 1) + | (leadingDistribution.index << 2); } @override @@ -230,7 +235,8 @@ class TextHeightBehavior { return false; return other is TextHeightBehavior && other.applyHeightToFirstAscent == applyHeightToFirstAscent - && other.applyHeightToLastDescent == applyHeightToLastDescent; + && other.applyHeightToLastDescent == applyHeightToLastDescent + && other.leadingDistribution == leadingDistribution; } @override @@ -245,7 +251,8 @@ class TextHeightBehavior { String toString() { return 'TextHeightBehavior(' 'applyHeightToFirstAscent: $applyHeightToFirstAscent, ' - 'applyHeightToLastDescent: $applyHeightToLastDescent' + 'applyHeightToLastDescent: $applyHeightToLastDescent, ' + 'leadingDistribution: $leadingDistribution' ')'; } } From f3e74f4868d27e62913e2c62e4594b85a59ce30e Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Wed, 24 Feb 2021 12:15:34 -0800 Subject: [PATCH 13/13] review --- lib/ui/text.dart | 34 ++++++++++--------- lib/web_ui/lib/src/engine/text/paragraph.dart | 4 +-- lib/web_ui/lib/src/ui/text.dart | 12 +++---- testing/dart/text_test.dart | 18 +++++----- third_party/txt/src/txt/paragraph_style.h | 4 +-- third_party/txt/src/txt/paragraph_txt.cc | 4 +-- third_party/txt/tests/paragraph_unittests.cc | 2 +- 7 files changed, 40 insertions(+), 38 deletions(-) diff --git a/lib/ui/text.dart b/lib/ui/text.dart index b779f743f4d59..54f794938e5be 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -1565,8 +1565,8 @@ enum TextDecorationStyle { wavy } -/// {@macro dart.ui.leadingDistribution} -enum LeadingDistribution { +/// {@macro dart.ui.textLeadingDistribution} +enum TextLeadingDistribution { /// Distributes the [leading](https://en.wikipedia.org/wiki/Leading) /// of the text proportionally above and below the text, to the font's /// ascent/discent ratio. @@ -1623,7 +1623,7 @@ class TextHeightBehavior { const TextHeightBehavior({ this.applyHeightToFirstAscent = true, this.applyHeightToLastDescent = true, - this.leadingDistribution = LeadingDistribution.proportional, + this.leadingDistribution = TextLeadingDistribution.proportional, }); /// Creates a new TextHeightBehavior object from an encoded form. @@ -1632,7 +1632,7 @@ class TextHeightBehavior { TextHeightBehavior.fromEncoded(int encoded) : applyHeightToFirstAscent = (encoded & 0x1) == 0, applyHeightToLastDescent = (encoded & 0x2) == 0, - leadingDistribution = LeadingDistribution.values[encoded >> 2]; + leadingDistribution = TextLeadingDistribution.values[encoded >> 2]; /// Whether to apply the [TextStyle.height] modifier to the ascent of the first /// line in the paragraph. @@ -1658,17 +1658,17 @@ class TextHeightBehavior { /// Defaults to true (height modifications applied as normal). final bool applyHeightToLastDescent; - /// {@template dart.ui.leadingDistribution} + /// {@template dart.ui.textLeadingDistribution} /// How the ["leading"](https://en.wikipedia.org/wiki/Leading) is distributed /// over and under the text. /// /// Does not affect layout when [TextStyle.height] is not specified. The - /// leading can become negative, for example, when [LeadingDistribution.even] + /// leading can become negative, for example, when [TextLeadingDistribution.even] /// is used with a [TextStyle.height] much smaller than 1.0. /// {@endtemplate} /// - /// Defaults to [LeadingDistribution.proportional], - final LeadingDistribution leadingDistribution; + /// Defaults to [TextLeadingDistribution.proportional], + final TextLeadingDistribution leadingDistribution; /// Returns an encoded int representation of this object. int encode() { @@ -1765,7 +1765,7 @@ Int32List _encodeTextStyle( double? letterSpacing, double? wordSpacing, double? height, - LeadingDistribution? leadingDistribution, + TextLeadingDistribution? leadingDistribution, Locale? locale, Paint? background, Paint? foreground, @@ -1903,7 +1903,7 @@ class TextStyle { double? letterSpacing, double? wordSpacing, double? height, - LeadingDistribution? leadingDistribution, + TextLeadingDistribution? leadingDistribution, Locale? locale, Paint? background, Paint? foreground, @@ -2006,7 +2006,7 @@ class TextStyle { 'letterSpacing: ${ _encoded[0] & 0x01000 == 0x01000 ? "${_letterSpacing}x" : "unspecified"}, ' 'wordSpacing: ${ _encoded[0] & 0x02000 == 0x02000 ? "${_wordSpacing}x" : "unspecified"}, ' 'height: ${ _encoded[0] & 0x04000 == 0x04000 ? "${_height}x" : "unspecified"}, ' - 'leadingDistribution: ${_encoded[0] & 0x0100 == 0x0100 ? "${LeadingDistribution.values[_encoded[8]]}" : "unspecified"}, ' + 'leadingDistribution: ${_encoded[0] & 0x0100 == 0x0100 ? "${TextLeadingDistribution.values[_encoded[8]]}" : "unspecified"}, ' 'locale: ${ _encoded[0] & 0x08000 == 0x08000 ? _locale : "unspecified"}, ' 'background: ${ _encoded[0] & 0x10000 == 0x10000 ? _background : "unspecified"}, ' 'foreground: ${ _encoded[0] & 0x20000 == 0x20000 ? _foreground : "unspecified"}, ' @@ -2259,7 +2259,7 @@ ByteData _encodeStrut( List? fontFamilyFallback, double? fontSize, double? height, - LeadingDistribution? leadingDistribution, + TextLeadingDistribution? leadingDistribution, double? leading, FontWeight? fontWeight, FontStyle? fontStyle, @@ -2350,10 +2350,12 @@ class StrutStyle { /// be provided for this property to take effect. /// /// * `leading`: The minimum amount of leading between lines as a multiple of - /// the font size. `fontSize` must be provided for this property to take effect. + /// the font size. `fontSize` must be provided for this property to take + /// effect. The leading added by this property is distributed evenly over + /// and under the text, regardless of `leadingDistribution`. /// - /// * `leadingDistribution`: Specifies how the extra vertical space added by - /// the `height` multiplier should be distributed over and under the text, + /// * `leadingDistribution`: how the extra vertical space added by the + /// `height` multiplier should be distributed over and under the text, /// independent of `leading` (which is always distributed evenly over and /// under text). /// @@ -2375,7 +2377,7 @@ class StrutStyle { List? fontFamilyFallback, double? fontSize, double? height, - LeadingDistribution? leadingDistribution, + TextLeadingDistribution? leadingDistribution, double? leading, FontWeight? fontWeight, FontStyle? fontStyle, diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 6afc59589d25f..463e763b8eefe 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -1212,7 +1212,7 @@ class EngineStrutStyle implements ui.StrutStyle { double? fontSize, double? height, //TODO(LongCatIsLooong): implement leadingDistribution. - ui.LeadingDistribution? leadingDistribution, + ui.TextLeadingDistribution? leadingDistribution, double? leading, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, @@ -1235,7 +1235,7 @@ class EngineStrutStyle implements ui.StrutStyle { final ui.FontWeight? _fontWeight; final ui.FontStyle? _fontStyle; final bool? _forceStrutHeight; - final ui.LeadingDistribution? _leadingDistribution; + final ui.TextLeadingDistribution? _leadingDistribution; @override bool operator ==(Object other) { diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index 64de60dd71ae4..c94308e6369ab 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -204,7 +204,7 @@ enum TextDecorationStyle { wavy } -enum LeadingDistribution { +enum TextLeadingDistribution { proportional, even, } @@ -213,15 +213,15 @@ class TextHeightBehavior { const TextHeightBehavior({ this.applyHeightToFirstAscent = true, this.applyHeightToLastDescent = true, - this.leadingDistribution = LeadingDistribution.proportional, + this.leadingDistribution = TextLeadingDistribution.proportional, }); TextHeightBehavior.fromEncoded(int encoded) : applyHeightToFirstAscent = (encoded & 0x1) == 0, applyHeightToLastDescent = (encoded & 0x2) == 0, - leadingDistribution = LeadingDistribution.values[encoded >> 2]; + leadingDistribution = TextLeadingDistribution.values[encoded >> 2]; final bool applyHeightToFirstAscent; final bool applyHeightToLastDescent; - final LeadingDistribution leadingDistribution; + final TextLeadingDistribution leadingDistribution; int encode() { return (applyHeightToFirstAscent ? 0 : 1 << 0) @@ -273,7 +273,7 @@ abstract class TextStyle { double? letterSpacing, double? wordSpacing, double? height, - LeadingDistribution? leadingDistribution, + TextLeadingDistribution? leadingDistribution, Locale? locale, Paint? background, Paint? foreground, @@ -384,7 +384,7 @@ abstract class StrutStyle { List? fontFamilyFallback, double? fontSize, double? height, - LeadingDistribution? leadingDistribution, + TextLeadingDistribution? leadingDistribution, double? leading, FontWeight? fontWeight, FontStyle? fontStyle, diff --git a/testing/dart/text_test.dart b/testing/dart/text_test.dart index ed685fe59a34b..9d4218e33db83 100644 --- a/testing/dart/text_test.dart +++ b/testing/dart/text_test.dart @@ -47,7 +47,7 @@ void main() { final TextStyle ts1 = TextStyle(color: const Color(0xFF00FF00), fontWeight: FontWeight.w800, fontSize: 10.0, height: 100.0); final TextStyle ts2 = TextStyle(fontFamily: 'test'); final TextStyle ts3 = TextStyle(fontFamily: 'foo', fontFamilyFallback: ['Roboto', 'test']); - final TextStyle ts4 = TextStyle(leadingDistribution: LeadingDistribution.even); + final TextStyle ts4 = TextStyle(leadingDistribution: TextLeadingDistribution.even); test('toString works', () { expect( @@ -68,7 +68,7 @@ void main() { ); expect( ts4.toString(), - equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, leadingDistribution: LeadingDistribution.even, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified)'), + equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, leadingDistribution: TextLeadingDistribution.even, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified)'), ); }); }); @@ -87,7 +87,7 @@ void main() { ); const TextHeightBehavior behavior4 = TextHeightBehavior( applyHeightToLastDescent: false, - leadingDistribution: LeadingDistribution.even, + leadingDistribution: TextLeadingDistribution.even, ); test('default constructor works', () { @@ -104,7 +104,7 @@ void main() { expect(behavior3.applyHeightToLastDescent, equals(false)); expect(behavior4.applyHeightToLastDescent, equals(false)); - expect(behavior4.leadingDistribution, equals(LeadingDistribution.even)); + expect(behavior4.leadingDistribution, equals(TextLeadingDistribution.even)); }); test('encode works', () { @@ -124,11 +124,11 @@ void main() { }); test('toString works', () { - expect(behavior0.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: true, leadingDistribution: LeadingDistribution.proportional)')); - expect(behavior1.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: false, leadingDistribution: LeadingDistribution.proportional)')); - expect(behavior2.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: true, leadingDistribution: LeadingDistribution.proportional)')); - expect(behavior3.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false, leadingDistribution: LeadingDistribution.proportional)')); - expect(behavior4.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false, leadingDistribution: LeadingDistribution.even)')); + expect(behavior0.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: true, leadingDistribution: TextLeadingDistribution.proportional)')); + expect(behavior1.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: false, leadingDistribution: TextLeadingDistribution.proportional)')); + expect(behavior2.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: true, leadingDistribution: TextLeadingDistribution.proportional)')); + expect(behavior3.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false, leadingDistribution: TextLeadingDistribution.proportional)')); + expect(behavior4.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false, leadingDistribution: TextLeadingDistribution.even)')); }); }); diff --git a/third_party/txt/src/txt/paragraph_style.h b/third_party/txt/src/txt/paragraph_style.h index d60d2638bf055..90c54ee4b23d8 100644 --- a/third_party/txt/src/txt/paragraph_style.h +++ b/third_party/txt/src/txt/paragraph_style.h @@ -48,7 +48,7 @@ enum class TextDirection { // the line will use the default font metric provided ascent/descent and // ParagraphStyle.height or TextStyle.height will not take effect. // -// kHalfLeading determines how the leading is distributed over and under the +// kEvenLeading determines how the leading is distributed over and under the // text. When true, half of the leading is added to the top of the text and the // other half is added to the bottom of the text. Otherwise, instead of // distributing the space evenly, it's distributed proportionally to the font's @@ -66,7 +66,7 @@ enum TextHeightBehavior { kDisableFirstAscent = 0x1, kDisableLastDescent = 0x2, kDisableAll = 0x1 | 0x2, - kHalfLeading = 0x1 << 2, + kEvenLeading = 0x1 << 2, }; class ParagraphStyle { diff --git a/third_party/txt/src/txt/paragraph_txt.cc b/third_party/txt/src/txt/paragraph_txt.cc index b14a94f962795..81f34ec8f42f0 100644 --- a/third_party/txt/src/txt/paragraph_txt.cc +++ b/third_party/txt/src/txt/paragraph_txt.cc @@ -570,7 +570,7 @@ void ParagraphTxt::ComputeStrut(StrutMetrics* strut, SkFont& font) { paragraph_style_.strut_has_leading_distribution_override ? paragraph_style_.strut_half_leading : paragraph_style_.text_height_behavior & - TextHeightBehavior::kHalfLeading; + TextHeightBehavior::kEvenLeading; const double available_height = half_leading_enabled ? metrics_height : strut_height; @@ -1215,7 +1215,7 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics, style.has_leading_distribution_override ? style.half_leading : paragraph_style_.text_height_behavior & - TextHeightBehavior::kHalfLeading; + TextHeightBehavior::kEvenLeading; // Scale the ascent and descent such that the sum of ascent and // descent is `style.height * style.font_size`. diff --git a/third_party/txt/tests/paragraph_unittests.cc b/third_party/txt/tests/paragraph_unittests.cc index fa9d0500138ef..81e2f64113345 100644 --- a/third_party/txt/tests/paragraph_unittests.cc +++ b/third_party/txt/tests/paragraph_unittests.cc @@ -1892,7 +1892,7 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(HeightOverrideHalfLeadingParagraph)) { txt::ParagraphStyle paragraph_style; paragraph_style.max_lines = 10; - paragraph_style.text_height_behavior = TextHeightBehavior::kHalfLeading; + paragraph_style.text_height_behavior = TextHeightBehavior::kEvenLeading; txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style;