未验证 提交 ee297aa6 编写于 作者: G Gary Qian 提交者: GitHub

Reland "Engine/LibTxt/dart:ui impl of TextHeightBehavior #15087" (#16155)

上级 4a55b4fd
......@@ -429,6 +429,94 @@ enum TextDecorationStyle {
wavy
}
/// {@template flutter.dart:ui.textHeightBehavior}
/// Defines how the paragraph will apply [TextStyle.height] to the ascent of the
/// first line and descent of the last line.
///
/// 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 {
/// Creates a new TextHeightBehavior object.
///
/// * applyHeightToFirstAscent: When true, the [TextStyle.height] modifier
/// will be applied to the ascent of the first line. When false, the font's
/// default ascent will be used.
/// * 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.
///
/// All properties default to true (height modifications applied as normal).
const TextHeightBehavior({
this.applyHeightToFirstAscent = true,
this.applyHeightToLastDescent = true,
});
/// Creates a new TextHeightBehavior object from an encoded form.
///
/// See [encode] for the creation of the encoded form.
const TextHeightBehavior.fromEncoded(int encoded) : applyHeightToFirstAscent = (encoded & 0x1) == 0,
applyHeightToLastDescent = (encoded & 0x2) == 0;
/// Whether to apply the [TextStyle.height] modifier to the ascent of the first
/// line in the paragraph.
///
/// When true, the [TextStyle.height] modifier will be applied to to the ascent
/// of the first line. When false, the font's default ascent will be used and
/// the [TextStyle.height] will have no effect on the ascent of the first line.
///
/// This property only has effect if a non-null [TextStyle.height] is specified.
///
/// Defaults to true (height modifications applied as normal).
final bool applyHeightToFirstAscent;
/// Whether to apply the [TextStyle.height] modifier to the descent of the last
/// line in the paragraph.
///
/// When true, the [TextStyle.height] modifier will be applied to to the descent
/// of the last line. When false, the font's default descent will be used and
/// the [TextStyle.height] will have no effect on the descent of the last line.
///
/// This property only has effect if a non-null [TextStyle.height] is specified.
///
/// Defaults to true (height modifications applied as normal).
final bool applyHeightToLastDescent;
/// Returns an encoded int representation of this object.
int encode() {
return (applyHeightToFirstAscent ? 0 : 1 << 0) | (applyHeightToLastDescent ? 0 : 1 << 1);
}
@override
bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType)
return false;
return other is TextHeightBehavior
&& other.applyHeightToFirstAscent == applyHeightToFirstAscent
&& other.applyHeightToLastDescent == applyHeightToLastDescent;
}
@override
int get hashCode {
return hashValues(
applyHeightToFirstAscent,
applyHeightToLastDescent,
);
}
@override
String toString() {
return 'TextHeightBehavior('
'applyHeightToFirstAscent: $applyHeightToFirstAscent, '
'applyHeightToLastDescent: $applyHeightToLastDescent'
')';
}
}
/// Determines if lists [a] and [b] are deep equivalent.
///
/// Returns true if the lists are both null, or if they are both non-null, have
......@@ -746,6 +834,8 @@ class TextStyle {
//
// - Element 5: The value of |maxLines|.
//
// - Element 6: The encoded value of |textHeightBehavior|.
//
Int32List _encodeParagraphStyle(
TextAlign textAlign,
TextDirection textDirection,
......@@ -753,13 +843,14 @@ Int32List _encodeParagraphStyle(
String fontFamily,
double fontSize,
double height,
TextHeightBehavior textHeightBehavior,
FontWeight fontWeight,
FontStyle fontStyle,
StrutStyle strutStyle,
String ellipsis,
Locale locale,
) {
final Int32List result = Int32List(6); // also update paragraph_builder.cc
final Int32List result = Int32List(7); // also update paragraph_builder.cc
if (textAlign != null) {
result[0] |= 1 << 1;
result[1] = textAlign.index;
......@@ -780,28 +871,32 @@ Int32List _encodeParagraphStyle(
result[0] |= 1 << 5;
result[5] = maxLines;
}
if (fontFamily != null) {
if (textHeightBehavior != null) {
result[0] |= 1 << 6;
result[6] = textHeightBehavior.encode();
}
if (fontFamily != null) {
result[0] |= 1 << 7;
// Passed separately to native.
}
if (fontSize != null) {
result[0] |= 1 << 7;
result[0] |= 1 << 8;
// Passed separately to native.
}
if (height != null) {
result[0] |= 1 << 8;
result[0] |= 1 << 9;
// Passed separately to native.
}
if (strutStyle != null) {
result[0] |= 1 << 9;
result[0] |= 1 << 10;
// Passed separately to native.
}
if (ellipsis != null) {
result[0] |= 1 << 10;
result[0] |= 1 << 11;
// Passed separately to native.
}
if (locale != null) {
result[0] |= 1 << 11;
result[0] |= 1 << 12;
// Passed separately to native.
}
return result;
......@@ -842,6 +937,9 @@ class ParagraphStyle {
/// the line height to take the height as defined by the font, which may not
/// 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.
///
/// * `fontWeight`: The typeface thickness to use when painting the text
/// (e.g., bold).
///
......@@ -869,6 +967,7 @@ class ParagraphStyle {
String fontFamily,
double fontSize,
double height,
TextHeightBehavior textHeightBehavior,
FontWeight fontWeight,
FontStyle fontStyle,
StrutStyle strutStyle,
......@@ -881,6 +980,7 @@ class ParagraphStyle {
fontFamily,
fontSize,
height,
textHeightBehavior,
fontWeight,
fontStyle,
strutStyle,
......@@ -929,11 +1029,14 @@ class ParagraphStyle {
'fontWeight: ${ _encoded[0] & 0x008 == 0x008 ? FontWeight.values[_encoded[3]] : "unspecified"}, '
'fontStyle: ${ _encoded[0] & 0x010 == 0x010 ? FontStyle.values[_encoded[4]] : "unspecified"}, '
'maxLines: ${ _encoded[0] & 0x020 == 0x020 ? _encoded[5] : "unspecified"}, '
'fontFamily: ${ _encoded[0] & 0x040 == 0x040 ? _fontFamily : "unspecified"}, '
'fontSize: ${ _encoded[0] & 0x080 == 0x080 ? _fontSize : "unspecified"}, '
'height: ${ _encoded[0] & 0x100 == 0x100 ? "${_height}x" : "unspecified"}, '
'ellipsis: ${ _encoded[0] & 0x200 == 0x200 ? "\"$_ellipsis\"" : "unspecified"}, '
'locale: ${ _encoded[0] & 0x400 == 0x400 ? _locale : "unspecified"}'
'textHeightBehavior: ${
_encoded[0] & 0x040 == 0x040 ?
TextHeightBehavior.fromEncoded(_encoded[6]).toString() : "unspecified"}, '
'fontFamily: ${ _encoded[0] & 0x080 == 0x080 ? _fontFamily : "unspecified"}, '
'fontSize: ${ _encoded[0] & 0x100 == 0x100 ? _fontSize : "unspecified"}, '
'height: ${ _encoded[0] & 0x200 == 0x200 ? "${_height}x" : "unspecified"}, '
'ellipsis: ${ _encoded[0] & 0x400 == 0x400 ? "\"$_ellipsis\"" : "unspecified"}, '
'locale: ${ _encoded[0] & 0x800 == 0x800 ? _locale : "unspecified"}'
')';
}
}
......
......@@ -75,12 +75,13 @@ const int psTextDirectionIndex = 2;
const int psFontWeightIndex = 3;
const int psFontStyleIndex = 4;
const int psMaxLinesIndex = 5;
const int psFontFamilyIndex = 6;
const int psFontSizeIndex = 7;
const int psHeightIndex = 8;
const int psStrutStyleIndex = 9;
const int psEllipsisIndex = 10;
const int psLocaleIndex = 11;
const int psTextHeightBehaviorIndex = 6;
const int psFontFamilyIndex = 7;
const int psFontSizeIndex = 8;
const int psHeightIndex = 9;
const int psStrutStyleIndex = 10;
const int psEllipsisIndex = 11;
const int psLocaleIndex = 12;
const int psTextAlignMask = 1 << psTextAlignIndex;
const int psTextDirectionMask = 1 << psTextDirectionIndex;
......@@ -90,6 +91,7 @@ const int psMaxLinesMask = 1 << psMaxLinesIndex;
const int psFontFamilyMask = 1 << psFontFamilyIndex;
const int psFontSizeMask = 1 << psFontSizeIndex;
const int psHeightMask = 1 << psHeightIndex;
const int psTextHeightBehaviorMask = 1 << psTextHeightBehaviorIndex;
const int psStrutStyleMask = 1 << psStrutStyleIndex;
const int psEllipsisMask = 1 << psEllipsisIndex;
const int psLocaleMask = 1 << psLocaleIndex;
......@@ -265,6 +267,10 @@ ParagraphBuilder::ParagraphBuilder(
style.has_height_override = true;
}
if (mask & psTextHeightBehaviorMask) {
style.text_height_behavior = encoded[psTextHeightBehaviorIndex];
}
if (mask & psStrutStyleMask) {
decodeStrut(strutData, strutFontFamilies, style);
}
......
......@@ -25,6 +25,7 @@ class SkParagraphStyle implements ui.ParagraphStyle {
String fontFamily,
double fontSize,
double height,
ui.TextHeightBehavior textHeightBehavior,
ui.FontWeight fontWeight,
ui.FontStyle fontStyle,
ui.StrutStyle strutStyle,
......@@ -38,6 +39,7 @@ class SkParagraphStyle implements ui.ParagraphStyle {
fontFamily,
fontSize,
height,
textHeightBehavior,
fontWeight,
fontStyle,
ellipsis,
......@@ -85,6 +87,7 @@ class SkParagraphStyle implements ui.ParagraphStyle {
String fontFamily,
double fontSize,
double height,
ui.TextHeightBehavior textHeightBehavior,
ui.FontWeight fontWeight,
ui.FontStyle fontStyle,
String ellipsis,
......@@ -129,6 +132,10 @@ class SkParagraphStyle implements ui.ParagraphStyle {
skParagraphStyle['heightMultiplier'] = height;
}
if (textHeightBehavior != null) {
skParagraphStyle['textHeightBehavior'] = textHeightBehavior.encode();
}
if (maxLines != null) {
skParagraphStyle['maxLines'] = maxLines;
}
......
......@@ -468,6 +468,7 @@ class EngineParagraphStyle implements ui.ParagraphStyle {
String fontFamily,
double fontSize,
double height,
ui.TextHeightBehavior textHeightBehavior,
ui.FontWeight fontWeight,
ui.FontStyle fontStyle,
ui.StrutStyle strutStyle,
......@@ -481,6 +482,7 @@ class EngineParagraphStyle implements ui.ParagraphStyle {
_fontFamily = fontFamily,
_fontSize = fontSize,
_height = height,
_textHeightBehavior = textHeightBehavior,
// TODO(b/128317744): add support for strut style.
_strutStyle = strutStyle,
_ellipsis = ellipsis,
......@@ -494,6 +496,7 @@ class EngineParagraphStyle implements ui.ParagraphStyle {
final String _fontFamily;
final double _fontSize;
final double _height;
final ui.TextHeightBehavior _textHeightBehavior;
final EngineStrutStyle _strutStyle;
final String _ellipsis;
final ui.Locale _locale;
......@@ -547,13 +550,27 @@ class EngineParagraphStyle implements ui.ParagraphStyle {
_fontFamily == typedOther._fontFamily ||
_fontSize == typedOther._fontSize ||
_height == typedOther._height ||
_textHeightBehavior == typedOther._textHeightBehavior ||
_ellipsis == typedOther._ellipsis ||
_locale == typedOther._locale;
}
@override
int get hashCode =>
ui.hashValues(_fontFamily, _fontSize, _height, _ellipsis, _locale);
int get hashCode {
return ui.hashValues(
_textAlign,
_textDirection,
_fontWeight,
_fontStyle,
_maxLines,
_fontFamily,
_fontSize,
_height,
_textHeightBehavior,
_ellipsis,
_locale
);
}
@override
String toString() {
......@@ -564,6 +581,7 @@ class EngineParagraphStyle implements ui.ParagraphStyle {
'fontWeight: ${_fontWeight ?? "unspecified"}, '
'fontStyle: ${_fontStyle ?? "unspecified"}, '
'maxLines: ${_maxLines ?? "unspecified"}, '
'textHeightBehavior: ${_textHeightBehavior ?? "unspecified"}, '
'fontFamily: ${_fontFamily ?? "unspecified"}, '
'fontSize: ${_fontSize != null ? _fontSize.toStringAsFixed(1) : "unspecified"}, '
'height: ${_height != null ? "${_height.toStringAsFixed(1)}x" : "unspecified"}, '
......
......@@ -424,6 +424,92 @@ enum TextDecorationStyle {
wavy
}
/// Defines how the paragraph will apply [TextStyle.height] the ascent of the
/// first line and descent of the last line.
///
/// The 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.
class TextHeightBehavior {
/// Creates a new TextHeightBehavior object.
///
/// * applyHeightToFirstAscent: When true, the [TextStyle.height] modifier
/// will be applied to the ascent of the first line. When false, the font's
/// default ascent will be used.
/// * 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.
///
/// All properties default to true (height modifications applied as normal).
const TextHeightBehavior({
this.applyHeightToFirstAscent = true,
this.applyHeightToLastDescent = true,
});
/// Creates a new TextHeightBehavior object from an encoded form.
///
/// See [encode] for the creation of the encoded form.
const TextHeightBehavior.fromEncoded(int encoded) : applyHeightToFirstAscent = (encoded & 0x1) == 0,
applyHeightToLastDescent = (encoded & 0x2) == 0;
/// Whether to apply the [TextStyle.height] modifier to the ascent of the first
/// line in the paragraph.
///
/// When true, the [TextStyle.height] modifier will be applied to to the ascent
/// of the first line. When false, the font's default ascent will be used and
/// the [TextStyle.height] will have no effect on the ascent of the first line.
///
/// This property only has effect if a non-null [TextStyle.height] is specified.
///
/// Defaults to true (height modifications applied as normal).
final bool applyHeightToFirstAscent;
/// Whether to apply the [TextStyle.height] modifier to the descent of the last
/// line in the paragraph.
///
/// When true, the [TextStyle.height] modifier will be applied to to the descent
/// of the last line. When false, the font's default descent will be used and
/// the [TextStyle.height] will have no effect on the descent of the last line.
///
/// This property only has effect if a non-null [TextStyle.height] is specified.
///
/// Defaults to true (height modifications applied as normal).
final bool applyHeightToLastDescent;
/// Returns an encoded int representation of this object.
int encode() {
return (applyHeightToFirstAscent ? 0 : 1 << 0) | (applyHeightToLastDescent ? 0 : 1 << 1);
}
@override
bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType)
return false;
return other is TextHeightBehavior
&& other.applyHeightToFirstAscent == applyHeightToFirstAscent
&& other.applyHeightToLastDescent == applyHeightToLastDescent;
}
@override
int get hashCode {
return hashValues(
applyHeightToFirstAscent,
applyHeightToLastDescent,
);
}
@override
String toString() {
return 'TextHeightBehavior('
'applyHeightToFirstAscent: $applyHeightToFirstAscent, '
'applyHeightToLastDescent: $applyHeightToLastDescent'
')';
}
}
/// An opaque object that determines the size, position, and rendering of text.
abstract class TextStyle {
/// Creates a new TextStyle object.
......@@ -584,6 +670,7 @@ abstract class ParagraphStyle {
String fontFamily,
double fontSize,
double height,
TextHeightBehavior textHeightBehavior,
FontWeight fontWeight,
FontStyle fontStyle,
StrutStyle strutStyle,
......@@ -598,6 +685,7 @@ abstract class ParagraphStyle {
fontFamily: fontFamily,
fontSize: fontSize,
height: height,
textHeightBehavior: textHeightBehavior,
fontWeight: fontWeight,
fontStyle: fontStyle,
strutStyle: strutStyle,
......@@ -612,6 +700,7 @@ abstract class ParagraphStyle {
fontFamily: fontFamily,
fontSize: fontSize,
height: height,
textHeightBehavior: textHeightBehavior,
fontWeight: fontWeight,
fontStyle: fontStyle,
strutStyle: strutStyle,
......
......@@ -26,6 +26,70 @@ void main() {
expect(FontWeight.lerp(FontWeight.w400, null, 1), equals(FontWeight.w400));
});
});
group('ParagraphStyle', () {
final ParagraphStyle ps0 = ParagraphStyle(textDirection: TextDirection.ltr, fontSize: 14.0);
final ParagraphStyle ps1 = ParagraphStyle(textDirection: TextDirection.rtl, fontSize: 14.0);
final ParagraphStyle ps2 = ParagraphStyle(textAlign: TextAlign.center, fontWeight: FontWeight.w800, fontSize: 10.0, height: 100.0);
final ParagraphStyle ps3 = ParagraphStyle(fontWeight: FontWeight.w700, fontSize: 12.0, height: 123.0);
test('toString works', () {
expect(ps0.toString(), equals('ParagraphStyle(textAlign: unspecified, textDirection: TextDirection.ltr, fontWeight: unspecified, fontStyle: unspecified, maxLines: unspecified, textHeightBehavior: unspecified, fontFamily: unspecified, fontSize: 14.0, height: unspecified, ellipsis: unspecified, locale: unspecified)'));
expect(ps1.toString(), equals('ParagraphStyle(textAlign: unspecified, textDirection: TextDirection.rtl, fontWeight: unspecified, fontStyle: unspecified, maxLines: unspecified, textHeightBehavior: unspecified, fontFamily: unspecified, fontSize: 14.0, height: unspecified, ellipsis: unspecified, locale: unspecified)'));
expect(ps2.toString(), equals('ParagraphStyle(textAlign: TextAlign.center, textDirection: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, maxLines: unspecified, textHeightBehavior: unspecified, fontFamily: unspecified, fontSize: 10.0, height: 100.0x, ellipsis: unspecified, locale: unspecified)'));
expect(ps3.toString(), equals('ParagraphStyle(textAlign: unspecified, textDirection: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, maxLines: unspecified, textHeightBehavior: unspecified, fontFamily: unspecified, fontSize: 12.0, height: 123.0x, ellipsis: unspecified, locale: unspecified)'));
});
});
group('TextHeightBehavior', () {
const TextHeightBehavior behavior0 = TextHeightBehavior();
const TextHeightBehavior behavior1 = TextHeightBehavior(
applyHeightToFirstAscent: false,
applyHeightToLastDescent: false
);
const TextHeightBehavior behavior2 = TextHeightBehavior(
applyHeightToFirstAscent: false,
);
const TextHeightBehavior behavior3 = TextHeightBehavior(
applyHeightToLastDescent: false
);
test('default constructor works', () {
expect(behavior0.applyHeightToFirstAscent, equals(true));
expect(behavior0.applyHeightToLastDescent, equals(true));
expect(behavior1.applyHeightToFirstAscent, equals(false));
expect(behavior1.applyHeightToLastDescent, equals(false));
expect(behavior2.applyHeightToFirstAscent, equals(false));
expect(behavior2.applyHeightToLastDescent, equals(true));
expect(behavior3.applyHeightToFirstAscent, equals(true));
expect(behavior3.applyHeightToLastDescent, equals(false));
});
test('encode works', () {
expect(behavior0.encode(), equals(0));
expect(behavior1.encode(), equals(3));
expect(behavior2.encode(), equals(1));
expect(behavior3.encode(), equals(2));
});
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));
});
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)'));
});
});
group('TextRange', () {
test('empty ranges are correct', () {
const TextRange range = TextRange(start: -1, end: -1);
......@@ -81,6 +145,7 @@ void main() {
expect(const TextRange(start: 0, end: 5).textInside('hello'), equals('hello'));
});
});
group('loadFontFromList', () {
test('will send platform message after font is loaded', () async {
final PlatformMessageCallback oldHandler = window.onPlatformMessage;
......
......@@ -41,6 +41,25 @@ 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:
// Default TextStyle. Used in GetTextStyle() to obtain the base TextStyle to
......@@ -50,6 +69,7 @@ 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;
// Strut properties. strut_enabled must be set to true for the rest of the
......
......@@ -1113,7 +1113,8 @@ void ParagraphTxt::Layout(double width) {
for (const PaintRecord& paint_record : paint_records) {
UpdateLineMetrics(paint_record.metrics(), paint_record.style(),
max_ascent, max_descent, max_unscaled_ascent,
paint_record.GetPlaceholderRun());
paint_record.GetPlaceholderRun(), line_number,
line_limit);
}
// If no fonts were actually rendered, then compute a baseline based on the
......@@ -1125,7 +1126,7 @@ void ParagraphTxt::Layout(double width) {
font.setSize(style.font_size);
font.getMetrics(&metrics);
UpdateLineMetrics(metrics, style, max_ascent, max_descent,
max_unscaled_ascent, nullptr);
max_unscaled_ascent, nullptr, line_number, line_limit);
}
// Calculate the baselines. This is only done on the first line.
......@@ -1181,7 +1182,9 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics,
double& max_ascent,
double& max_descent,
double& max_unscaled_ascent,
PlaceholderRun* placeholder_run) {
PlaceholderRun* placeholder_run,
size_t line_number,
size_t line_limit) {
if (!strut_.force_strut) {
double ascent;
double descent;
......@@ -1242,6 +1245,21 @@ void ParagraphTxt::UpdateLineMetrics(const SkFontMetrics& metrics,
ascent = (-metrics.fAscent + metrics.fLeading / 2);
descent = (metrics.fDescent + metrics.fLeading / 2);
}
// Account for text_height_behavior in paragraph_style_.
//
// 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;
}
ComputePlaceholder(placeholder_run, ascent, descent);
max_ascent = std::max(ascent, max_ascent);
......
......@@ -162,6 +162,7 @@ class ParagraphTxt : public Paragraph {
FRIEND_TEST(ParagraphTest, FontFeaturesParagraph);
FRIEND_TEST(ParagraphTest, GetGlyphPositionAtCoordinateSegfault);
FRIEND_TEST(ParagraphTest, KhmerLineBreaker);
FRIEND_TEST(ParagraphTest, TextHeightBehaviorRectsParagraph);
// Starting data to layout.
std::vector<uint16_t> text_;
......@@ -368,7 +369,10 @@ class ParagraphTxt : public Paragraph {
double& max_ascent,
double& max_descent,
double& max_unscaled_ascent,
PlaceholderRun* placeholder_run);
PlaceholderRun* placeholder_run,
size_t line_number,
size_t line_limit);
// Calculate the starting X offset of a line based on the line's width and
// alignment.
double GetLineXOffset(double line_total_advance,
......
......@@ -5877,4 +5877,102 @@ TEST_F(ParagraphTest, KhmerLineBreaker) {
ASSERT_TRUE(Snapshot());
}
TEST_F(ParagraphTest, TextHeightBehaviorRectsParagraph) {
// clang-format off
const char* text =
"line1\nline2\nline3";
// clang-format on
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 =
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<std::string>(1, "Roboto");
text_style.font_size = 30;
text_style.height = 5;
text_style.has_height_override = true;
builder.PushStyle(text_style);
builder.AddText(u16_text);
builder.Pop();
auto paragraph = BuildParagraph(builder);
paragraph->Layout(GetTestCanvasWidth() - 300);
paragraph->Paint(GetCanvas(), 0, 0);
for (size_t i = 0; i < u16_text.length(); i++) {
ASSERT_EQ(paragraph->text_[i], u16_text[i]);
}
ASSERT_EQ(paragraph->records_.size(), 3ull);
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<txt::Paragraph::TextBox> 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);
// First line. Shorter due to disabled height modifications on first ascent.
boxes =
paragraph->GetRectsForRange(0, 3, rect_height_style, rect_width_style);
for (size_t i = 0; i < boxes.size(); ++i) {
GetCanvas()->drawRect(boxes[i].rect, paint);
}
EXPECT_EQ(boxes.size(), 1ull);
EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0);
EXPECT_FLOAT_EQ(boxes[0].rect.right(), 31.117188);
EXPECT_FLOAT_EQ(boxes[0].rect.top(), -0.08203125);
EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59);
EXPECT_FLOAT_EQ(boxes[0].rect.bottom() - boxes[0].rect.top(), 59.082031);
// Second line. Normal.
boxes =
paragraph->GetRectsForRange(6, 10, rect_height_style, rect_width_style);
for (size_t i = 0; i < boxes.size(); ++i) {
GetCanvas()->drawRect(boxes[i].rect, paint);
}
EXPECT_EQ(boxes.size(), 1ull);
EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0);
EXPECT_FLOAT_EQ(boxes[0].rect.right(), 47.011719);
EXPECT_FLOAT_EQ(boxes[0].rect.top(), 59);
EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 209);
EXPECT_FLOAT_EQ(boxes[0].rect.bottom() - boxes[0].rect.top(), 150);
// Third line. Shorter due to disabled height modifications on last descent
boxes =
paragraph->GetRectsForRange(12, 17, rect_height_style, rect_width_style);
for (size_t i = 0; i < boxes.size(); ++i) {
GetCanvas()->drawRect(boxes[i].rect, paint);
}
EXPECT_EQ(boxes.size(), 1ull);
EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0);
EXPECT_FLOAT_EQ(boxes[0].rect.right(), 63.859375);
EXPECT_FLOAT_EQ(boxes[0].rect.top(), 208.92578);
EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 335);
EXPECT_FLOAT_EQ(boxes[0].rect.bottom() - boxes[0].rect.top(), 126.07422);
ASSERT_TRUE(Snapshot());
}
} // namespace txt
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册