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

Support overriding font leading in TextStyle and LibTxt (#6927)

上级 25c6c1be
......@@ -256,6 +256,7 @@ Int32List _encodeTextStyle(
double letterSpacing,
double wordSpacing,
double height,
double leading,
Locale locale,
Paint background,
Paint foreground,
......@@ -310,22 +311,26 @@ Int32List _encodeTextStyle(
result[0] |= 1 << 12;
// Passed separately to native.
}
if (locale != null) {
if (leading != null) {
result[0] |= 1 << 13;
// Passed separately to native.
}
if (background != null) {
if (locale != null) {
result[0] |= 1 << 14;
// Passed separately to native.
}
if (foreground != null) {
if (background != null) {
result[0] |= 1 << 15;
// Passed separately to native.
}
if (shadows != null) {
if (foreground != null) {
result[0] |= 1 << 16;
// Passed separately to native.
}
if (shadows != null) {
result[0] |= 1 << 17;
// Passed separately to native.
}
return result;
}
......@@ -344,7 +349,8 @@ class TextStyle {
/// * `letterSpacing`: The amount of space (in logical pixels) to add between each letter.
/// * `wordSpacing`: The amount of space (in logical pixels) to add at each sequence of white-space (i.e. between each word).
/// * `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 multiple of the font size.
/// * `height`: The height of this text span, as a multiple of the sum of font size and leading.
/// * `leading`: Custom leading to use instead of the font-provided leading as a multiple of font size. When null, default font leading will be used. Leading is the additional spacing between lines.
/// * `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.
......@@ -361,6 +367,7 @@ class TextStyle {
double letterSpacing,
double wordSpacing,
double height,
double leading,
Locale locale,
Paint background,
Paint foreground,
......@@ -382,6 +389,7 @@ class TextStyle {
letterSpacing,
wordSpacing,
height,
leading,
locale,
background,
foreground,
......@@ -392,6 +400,7 @@ class TextStyle {
_letterSpacing = letterSpacing,
_wordSpacing = wordSpacing,
_height = height,
_leading = leading,
_locale = locale,
_background = background,
_foreground = foreground,
......@@ -403,6 +412,7 @@ class TextStyle {
final double _letterSpacing;
final double _wordSpacing;
final double _height;
final double _leading;
final Locale _locale;
final Paint _background;
final Paint _foreground;
......@@ -420,6 +430,7 @@ class TextStyle {
_letterSpacing != typedOther._letterSpacing ||
_wordSpacing != typedOther._wordSpacing ||
_height != typedOther._height ||
_leading != typedOther._leading ||
_locale != typedOther._locale ||
_background != typedOther._background ||
_foreground != typedOther._foreground)
......@@ -434,7 +445,7 @@ class TextStyle {
}
@override
int get hashCode => hashValues(hashList(_encoded), _fontFamily, _fontSize, _letterSpacing, _wordSpacing, _height, _locale, _background, _foreground);
int get hashCode => hashValues(hashList(_encoded), _fontFamily, _fontSize, _letterSpacing, _wordSpacing, _height, _leading, _locale, _background, _foreground);
@override
String toString() {
......@@ -451,10 +462,11 @@ class TextStyle {
'letterSpacing: ${ _encoded[0] & 0x00400 == 0x00400 ? "${_letterSpacing}x" : "unspecified"}, '
'wordSpacing: ${ _encoded[0] & 0x00800 == 0x00800 ? "${_wordSpacing}x" : "unspecified"}, '
'height: ${ _encoded[0] & 0x01000 == 0x01000 ? "${_height}x" : "unspecified"}, '
'locale: ${ _encoded[0] & 0x02000 == 0x02000 ? _locale : "unspecified"}, '
'background: ${ _encoded[0] & 0x04000 == 0x04000 ? _background : "unspecified"}, '
'foreground: ${ _encoded[0] & 0x08000 == 0x08000 ? _foreground : "unspecified"}, '
'shadows: ${ _encoded[0] & 0x10000 == 0x10000 ? _shadows : "unspecified"}'
'leading: ${ _encoded[0] & 0x02000 == 0x02000 ? "${_leading}x" : "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"}'
')';
}
}
......@@ -1135,8 +1147,8 @@ class ParagraphBuilder extends NativeFieldWrapperClass2 {
/// Applies the given style to the added text until [pop] is called.
///
/// See [pop] for details.
void pushStyle(TextStyle style) => _pushStyle(style._encoded, style._fontFamily, style._fontSize, style._letterSpacing, style._wordSpacing, style._height, _encodeLocale(style._locale), style._background?._objects, style._background?._data, style._foreground?._objects, style._foreground?._data, Shadow._encodeShadows(style._shadows));
void _pushStyle(Int32List encoded, String fontFamily, double fontSize, double letterSpacing, double wordSpacing, double height, String locale, List<dynamic> backgroundObjects, ByteData backgroundData, List<dynamic> foregroundObjects, ByteData foregroundData, ByteData shadowsData) native 'ParagraphBuilder_pushStyle';
void pushStyle(TextStyle style) => _pushStyle(style._encoded, style._fontFamily, style._fontSize, style._letterSpacing, style._wordSpacing, style._height, style._leading, _encodeLocale(style._locale), style._background?._objects, style._background?._data, style._foreground?._objects, style._foreground?._data, Shadow._encodeShadows(style._shadows));
void _pushStyle(Int32List encoded, String fontFamily, double fontSize, double letterSpacing, double wordSpacing, double height, double leading, String locale, List<dynamic> backgroundObjects, ByteData backgroundData, List<dynamic> foregroundObjects, ByteData foregroundData, ByteData shadowsData) native 'ParagraphBuilder_pushStyle';
static String _encodeLocale(Locale locale) => locale?.toString() ?? '';
......
......@@ -41,10 +41,11 @@ const int tsFontSizeIndex = 9;
const int tsLetterSpacingIndex = 10;
const int tsWordSpacingIndex = 11;
const int tsHeightIndex = 12;
const int tsLocaleIndex = 13;
const int tsBackgroundIndex = 14;
const int tsForegroundIndex = 15;
const int tsTextShadowsIndex = 16;
const int tsLeadingIndex = 13;
const int tsLocaleIndex = 14;
const int tsBackgroundIndex = 15;
const int tsForegroundIndex = 16;
const int tsTextShadowsIndex = 17;
const int tsColorMask = 1 << tsColorIndex;
const int tsTextDecorationMask = 1 << tsTextDecorationIndex;
......@@ -58,6 +59,7 @@ const int tsFontSizeMask = 1 << tsFontSizeIndex;
const int tsLetterSpacingMask = 1 << tsLetterSpacingIndex;
const int tsWordSpacingMask = 1 << tsWordSpacingIndex;
const int tsHeightMask = 1 << tsHeightIndex;
const int tsLeadingMask = 1 << tsLeadingIndex;
const int tsLocaleMask = 1 << tsLocaleIndex;
const int tsBackgroundMask = 1 << tsBackgroundIndex;
const int tsForegroundMask = 1 << tsForegroundIndex;
......@@ -207,6 +209,7 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded,
double letterSpacing,
double wordSpacing,
double height,
double leading,
const std::string& locale,
Dart_Handle background_objects,
Dart_Handle background_data,
......@@ -269,6 +272,11 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded,
style.height = height;
}
if (mask & tsLeadingMask) {
style.use_custom_leading = true;
style.leading = leading;
}
if (mask & tsLocaleMask) {
style.locale = locale;
}
......
......@@ -40,6 +40,7 @@ class ParagraphBuilder : public RefCountedDartWrappable<ParagraphBuilder> {
double letterSpacing,
double wordSpacing,
double height,
double leading,
const std::string& locale,
Dart_Handle background_objects,
Dart_Handle background_data,
......
......@@ -761,10 +761,13 @@ void Paragraph::Layout(double width, bool force) {
// TODO(garyq): Multipling in the style.height on the first line is
// probably wrong. Figure out how paragraph and line heights are supposed
// to work and fix it.
double line_spacing =
(line_number == 0)
? -metrics.fAscent * style.height
: (-metrics.fAscent + metrics.fLeading) * style.height;
double leading =
style.use_custom_leading
? (metrics.fDescent - metrics.fAscent) * style.leading
: metrics.fLeading;
double line_spacing = (line_number == 0)
? -metrics.fAscent * style.height
: (-metrics.fAscent + leading) * style.height;
if (line_spacing > max_line_spacing) {
max_line_spacing = line_spacing;
if (line_number == 0) {
......
......@@ -214,7 +214,10 @@ class Paragraph {
FRIEND_TEST(ParagraphTest, DecorationsParagraph);
FRIEND_TEST(ParagraphTest, ItalicsParagraph);
FRIEND_TEST(ParagraphTest, ChineseParagraph);
FRIEND_TEST(ParagraphTest, DISABLED_ArabicParagraph);
FRIEND_TEST(ParagraphTest, ArabicParagraph);
FRIEND_TEST(ParagraphTest, ArabicLeadingOverrideParagraph);
FRIEND_TEST(ParagraphTest, ArabicLeadingOverrideTallParagraph);
FRIEND_TEST(ParagraphTest, ArabicLeadingOverrideNegativeParagraph);
FRIEND_TEST(ParagraphTest, SpacingParagraph);
FRIEND_TEST(ParagraphTest, LongWordParagraph);
FRIEND_TEST(ParagraphTest, KernScaleParagraph);
......
......@@ -73,7 +73,10 @@ class StyledRuns {
FRIEND_TEST(ParagraphTest, DecorationsParagraph);
FRIEND_TEST(ParagraphTest, ItalicsParagraph);
FRIEND_TEST(ParagraphTest, ChineseParagraph);
FRIEND_TEST(ParagraphTest, DISABLED_ArabicParagraph);
FRIEND_TEST(ParagraphTest, ArabicParagraph);
FRIEND_TEST(ParagraphTest, ArabicLeadingOverrideParagraph);
FRIEND_TEST(ParagraphTest, ArabicLeadingOverrideTallParagraph);
FRIEND_TEST(ParagraphTest, ArabicLeadingOverrideNegativeParagraph);
FRIEND_TEST(ParagraphTest, LongWordParagraph);
FRIEND_TEST(ParagraphTest, KernParagraph);
FRIEND_TEST(ParagraphTest, HyphenBreakParagraph);
......
......@@ -47,6 +47,10 @@ bool TextStyle::equals(const TextStyle& other) const {
return false;
if (height != other.height)
return false;
if (use_custom_leading != other.use_custom_leading)
return false;
if (use_custom_leading && leading != other.leading)
return false;
if (locale != other.locale)
return false;
if (foreground != other.foreground)
......
......@@ -48,6 +48,8 @@ class TextStyle {
double letter_spacing = 0.0;
double word_spacing = 0.0;
double height = 1.0;
double leading = 0;
bool use_custom_leading = false;
std::string locale;
bool has_background = false;
SkPaint background;
......
......@@ -821,11 +821,15 @@ TEST_F(ParagraphTest, ChineseParagraph) {
ASSERT_TRUE(Snapshot());
}
// TODO(garyq): Support RTL languages.
TEST_F(ParagraphTest, DISABLED_ArabicParagraph) {
TEST_F(ParagraphTest, ArabicParagraph) {
const char* text =
"من أسر وإعلان الخاصّة وهولندا،, عل قائمة الضغوط بالمطالبة تلك. الصفحة "
"بمباركة التقليدية قام عن. تصفح";
"بمباركة التقليدية قام عن. تصفحبمباركة التقل. يدية فح قام عن. "
"تصفحبمباركة التقليدية قام عن. تصفحبمباركة التقليدية تصفحب قام عن. فح "
"تصفحبمقباركة التقلفحيدية قام عن. تصفحبمباركة التقليدية ققام عن. "
"تصفحبمباركة التقليدية قام عن. تصفحبمباركة التقليدية قام عن. تص تصفحب "
"فحبمباركة التقققليدية قام عن. تصفحبمباركة التقليدية قام عن. تصفحبمباركة "
"التقليدية قام عن. تصفحبمباركة التقليدية قام عن. تصفح";
auto icu_text = icu::UnicodeString::fromUTF8(text);
std::u16string u16_text(icu_text.getBuffer(),
icu_text.getBuffer() + icu_text.length());
......@@ -838,12 +842,9 @@ TEST_F(ParagraphTest, DISABLED_ArabicParagraph) {
txt::TextStyle text_style;
text_style.color = SK_ColorBLACK;
text_style.font_size = 35;
text_style.font_size = 45;
text_style.letter_spacing = 2;
text_style.font_family = "Katibeh";
text_style.decoration = TextDecoration::kUnderline |
TextDecoration::kOverline |
TextDecoration::kLineThrough;
text_style.decoration_style = txt::TextDecorationStyle::kSolid;
text_style.decoration_color = SK_ColorBLACK;
builder.PushStyle(text_style);
......@@ -856,21 +857,190 @@ TEST_F(ParagraphTest, DISABLED_ArabicParagraph) {
paragraph->Layout(GetTestCanvasWidth() - 100);
paragraph->Paint(GetCanvas(), 0, 0);
ASSERT_TRUE(Snapshot());
ASSERT_EQ(paragraph->text_.size(), std::string{text}.length());
ASSERT_EQ(paragraph->text_.size(), 458ull); // Arabic script uses ligatures
ASSERT_EQ(paragraph->runs_.runs_.size(), 1ull);
ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull);
ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style));
ASSERT_EQ(paragraph->records_[0].style().color, text_style.color);
ASSERT_EQ(paragraph->records_.size(), 2ull);
ASSERT_EQ(paragraph->records_.size(),
8ull); // 8 lines, breaks into 8 records.
ASSERT_EQ(paragraph->paragraph_style_.text_direction, TextDirection::rtl);
for (size_t i = 0; i < u16_text.length(); i++) {
ASSERT_EQ(paragraph->text_[i], u16_text[u16_text.length() - i]);
}
ASSERT_EQ(paragraph->line_heights_[0], 45);
ASSERT_EQ(paragraph->line_heights_[1], 99);
ASSERT_EQ(paragraph->line_heights_[2], 153);
ASSERT_EQ(paragraph->line_heights_[3], 207);
}
TEST_F(ParagraphTest, ArabicLeadingOverrideParagraph) {
const char* text =
"من أسر وإعلان الخاصّة وهولندا،, عل قائمة الضغوط بالمطالبة تلك. الصفحة "
"بمباركة التقليدية قام عن. تصفحبمباركة التقل. يدية فح قام عن. "
"تصفحبمباركة التقليدية قام عن. تصفحبمباركة التقليدية تصفحب قام عن. فح "
"تصفحبمقباركة التقلفحيدية قام عن. تصفحبمباركة التقليدية ققام عن. "
"تصفحبمباركة التقليدية قام عن. تصفحبمباركة التقليدية قام عن. تص تصفحب "
"فحبمباركة التقققليدية قام عن. تصفحبمباركة التقليدية قام عن. تصفحبمباركة "
"التقليدية قام عن. تصفحبمباركة التقليدية قام عن. تصفح";
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 = 14;
paragraph_style.text_align = TextAlign::right;
paragraph_style.text_direction = TextDirection::rtl;
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());
txt::TextStyle text_style;
text_style.color = SK_ColorBLACK;
text_style.font_size = 45;
text_style.letter_spacing = 2;
text_style.font_family = "Katibeh";
text_style.decoration_style = txt::TextDecorationStyle::kSolid;
text_style.decoration_color = SK_ColorBLACK;
text_style.use_custom_leading = true;
text_style.leading = 0;
builder.PushStyle(text_style);
builder.AddText(u16_text);
builder.Pop();
auto paragraph = builder.Build();
paragraph->Layout(GetTestCanvasWidth() - 100);
paragraph->Paint(GetCanvas(), 0, 0);
ASSERT_TRUE(Snapshot());
ASSERT_EQ(paragraph->text_.size(), 458ull); // Arabic script uses ligatures
ASSERT_EQ(paragraph->runs_.runs_.size(), 1ull);
ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull);
ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style));
ASSERT_EQ(paragraph->records_[0].style().color, text_style.color);
ASSERT_EQ(paragraph->records_.size(),
8ull); // 8 lines, breaks into 8 records.
ASSERT_EQ(paragraph->paragraph_style_.text_direction, TextDirection::rtl);
ASSERT_EQ(paragraph->line_heights_[0], 45);
ASSERT_EQ(paragraph->line_heights_[1], 90);
ASSERT_EQ(paragraph->line_heights_[2], 135);
ASSERT_EQ(paragraph->line_heights_[3], 180);
}
TEST_F(ParagraphTest, ArabicLeadingOverrideTallParagraph) {
const char* text =
"من أسر وإعلان الخاصّة وهولندا،, عل قائمة الضغوط بالمطالبة تلك. الصفحة "
"بمباركة التقليدية قام عن. تصفحبمباركة التقل. يدية فح قام عن. "
"تصفحبمباركة التقليدية قام عن. تصفحبمباركة التقليدية تصفحب قام عن. فح "
"تصفحبمقباركة التقلفحيدية قام عن. تصفحبمباركة التقليدية ققام عن. "
"تصفحبمباركة التقليدية قام عن. تصفحبمباركة التقليدية قام عن. تص تصفحب "
"فحبمباركة التقققليدية قام عن. تصفحبمباركة التقليدية قام عن. تصفحبمباركة "
"التقليدية قام عن. تصفحبمباركة التقليدية قام عن. تصفح";
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 = 14;
paragraph_style.text_align = TextAlign::right;
paragraph_style.text_direction = TextDirection::rtl;
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());
txt::TextStyle text_style;
text_style.color = SK_ColorBLACK;
text_style.font_size = 45;
text_style.letter_spacing = 2;
text_style.font_family = "Katibeh";
text_style.decoration_style = txt::TextDecorationStyle::kSolid;
text_style.decoration_color = SK_ColorBLACK;
text_style.use_custom_leading = true;
text_style.leading = 0.65;
builder.PushStyle(text_style);
builder.AddText(u16_text);
builder.Pop();
auto paragraph = builder.Build();
paragraph->Layout(GetTestCanvasWidth() - 100);
paragraph->Paint(GetCanvas(), 0, 0);
ASSERT_TRUE(Snapshot());
ASSERT_EQ(paragraph->text_.size(), 458ull); // Arabic script uses ligatures
ASSERT_EQ(paragraph->runs_.runs_.size(), 1ull);
ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull);
ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style));
ASSERT_EQ(paragraph->records_[0].style().color, text_style.color);
ASSERT_EQ(paragraph->records_.size(),
8ull); // 8 lines, breaks into 8 records.
ASSERT_EQ(paragraph->paragraph_style_.text_direction, TextDirection::rtl);
ASSERT_EQ(paragraph->line_heights_[0], 45);
ASSERT_EQ(paragraph->line_heights_[1], 119);
ASSERT_EQ(paragraph->line_heights_[2], 193);
ASSERT_EQ(paragraph->line_heights_[3], 267);
}
TEST_F(ParagraphTest, ArabicLeadingOverrideNegativeParagraph) {
const char* text =
"من أسر وإعلان الخاصّة وهولندا،, عل قائمة الضغوط بالمطالبة تلك. الصفحة "
"بمباركة التقليدية قام عن. تصفحبمباركة التقل. يدية فح قام عن. "
"تصفحبمباركة التقليدية قام عن. تصفحبمباركة التقليدية تصفحب قام عن. فح "
"تصفحبمقباركة التقلفحيدية قام عن. تصفحبمباركة التقليدية ققام عن. "
"تصفحبمباركة التقليدية قام عن. تصفحبمباركة التقليدية قام عن. تص تصفحب "
"فحبمباركة التقققليدية قام عن. تصفحبمباركة التقليدية قام عن. تصفحبمباركة "
"التقليدية قام عن. تصفحبمباركة التقليدية قام عن. تصفح";
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 = 14;
paragraph_style.text_align = TextAlign::right;
paragraph_style.text_direction = TextDirection::rtl;
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());
txt::TextStyle text_style;
text_style.color = SK_ColorBLACK;
text_style.font_size = 45;
text_style.letter_spacing = 2;
text_style.font_family = "Katibeh";
text_style.decoration_style = txt::TextDecorationStyle::kSolid;
text_style.decoration_color = SK_ColorBLACK;
text_style.use_custom_leading = true;
text_style.leading = -0.2;
builder.PushStyle(text_style);
builder.AddText(u16_text);
builder.Pop();
auto paragraph = builder.Build();
paragraph->Layout(GetTestCanvasWidth() - 100);
paragraph->Paint(GetCanvas(), 0, 0);
ASSERT_TRUE(Snapshot());
ASSERT_EQ(paragraph->text_.size(), 458ull); // Arabic script uses ligatures
ASSERT_EQ(paragraph->runs_.runs_.size(), 1ull);
ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull);
ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style));
ASSERT_EQ(paragraph->records_[0].style().color, text_style.color);
ASSERT_EQ(paragraph->records_.size(),
8ull); // 8 lines, breaks into 8 records.
ASSERT_EQ(paragraph->paragraph_style_.text_direction, TextDirection::rtl);
ASSERT_EQ(paragraph->line_heights_[0], 45);
ASSERT_EQ(paragraph->line_heights_[1], 81);
ASSERT_EQ(paragraph->line_heights_[2], 117);
ASSERT_EQ(paragraph->line_heights_[3], 153);
}
TEST_F(ParagraphTest, GetGlyphPositionAtCoordinateParagraph) {
......@@ -1162,9 +1332,6 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeTight)) {
TEST_F(ParagraphTest,
DISABLE_ON_WINDOWS(GetRectsForRangeIncludeLineSpacingMiddle)) {
// const char* text =
// "12345, \"67890\" 12345 67890 12345 67890 12345 67890 12345 67890
// 12345 " "67890 12345";
const char* text =
"( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
" ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册