From 2e2f19d3e9dd9bbc4e97e6b79bd573ab67c65109 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Fri, 21 Dec 2018 19:07:01 -0800 Subject: [PATCH] Expose font fallback API in TextStyle, Roll engine 54a3577c0139..215ca1560088 (8 commits) (#25585) --- bin/internal/engine.version | 2 +- .../flutter/lib/src/painting/text_style.dart | 55 ++++++++++++++++++- .../flutter/test/material/theme_test.dart | 5 +- .../test/painting/text_style_test.dart | 45 +++++++++++++-- 4 files changed, 98 insertions(+), 9 deletions(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index e0bdfdb53e..46e4347008 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -54a3577c0139f659f5064d918057bc46b6cd0932 +215ca15600887ffeffef2df3f216e0189159150e diff --git a/packages/flutter/lib/src/painting/text_style.dart b/packages/flutter/lib/src/painting/text_style.dart index daad2f04a3..c8ffa320b9 100644 --- a/packages/flutter/lib/src/painting/text_style.dart +++ b/packages/flutter/lib/src/painting/text_style.dart @@ -250,8 +250,11 @@ class TextStyle extends Diagnosticable { this.decorationStyle, this.debugLabel, String fontFamily, + List fontFamilyFallback, String package, }) : fontFamily = package == null ? fontFamily : 'packages/$package/$fontFamily', + _fontFamilyFallback = fontFamilyFallback, + _package = package, assert(inherit != null), assert(color == null || foreground == null, _kColorForegroundWarning); @@ -279,8 +282,43 @@ class TextStyle extends Diagnosticable { /// 'packages/package_name/' (e.g. 'packages/cool_fonts/Roboto'). The /// prefixing is done by the constructor when the `package` argument is /// provided. + /// + /// The value provided in [fontFamily] will act as the preferred/first font + /// family that glyphs are looked for in, followed in order by the font families + /// in [fontFamilyFallback]. When [fontFamily] is null or not provided, the + /// first value in [fontFamilyFallback] acts as the preferred/first font + /// family. When neither is provided, then the default platform font will + /// be used. final String fontFamily; + /// The ordered list of font families to fall back on when a glyph cannot be + /// found in a higher priority font family. + /// + /// The value provided in [fontFamily] will act as the preferred/first font + /// family that glyphs are looked for in, followed in order by the font families + /// in [fontFamilyFallback]. If all font families are exhausted and no match + /// was found, the default platform font family will be used instead. + /// + /// When [fontFamily] is null or not provided, the first value in [fontFamilyFallback] + /// acts as the preferred/first font family. When neither is provided, then + /// the default platform font will be used. Providing and empty list or null + /// for this property is the same as omitting it. + /// + /// For example, if a glyph is not found in [fontFamily], then each font family + /// in [fontFamilyFallback] will be searched in order until it is found. If it + /// is not found, then a box will be drawn in its place. + /// + /// If the font is defined in a package, each font family in the list will be + /// prefixed with 'packages/package_name/' (e.g. 'packages/cool_fonts/Roboto'). + /// The package name should be provided by the `package` argument in the + /// constructor. + List get fontFamilyFallback => _package != null && _fontFamilyFallback != null ? _fontFamilyFallback.map((String str) => 'packages/$_package/$str').toList() : _fontFamilyFallback; + final List _fontFamilyFallback; + + // This is stored in order to prefix the fontFamilies in _fontFamilyFallback + // in the [fontFamilyFallback] getter. + final String _package; + /// The size of glyphs (in logical pixels) to use when painting the text. /// /// During painting, the [fontSize] is multiplied by the current @@ -392,6 +430,7 @@ class TextStyle extends Diagnosticable { TextStyle copyWith({ Color color, String fontFamily, + List fontFamilyFallback, double fontSize, FontWeight fontWeight, FontStyle fontStyle, @@ -419,6 +458,7 @@ class TextStyle extends Diagnosticable { inherit: inherit, color: this.foreground == null && foreground == null ? color ?? this.color : null, fontFamily: fontFamily ?? this.fontFamily, + fontFamilyFallback: fontFamilyFallback ?? this.fontFamilyFallback, fontSize: fontSize ?? this.fontSize, fontWeight: fontWeight ?? this.fontWeight, fontStyle: fontStyle ?? this.fontStyle, @@ -469,6 +509,7 @@ class TextStyle extends Diagnosticable { Color decorationColor, TextDecorationStyle decorationStyle, String fontFamily, + List fontFamilyFallback, double fontSizeFactor = 1.0, double fontSizeDelta = 0.0, int fontWeightDelta = 0, @@ -505,6 +546,7 @@ class TextStyle extends Diagnosticable { inherit: inherit, color: foreground == null ? color ?? this.color : null, fontFamily: fontFamily ?? this.fontFamily, + fontFamilyFallback: fontFamilyFallback ?? this.fontFamilyFallback, fontSize: fontSize == null ? null : fontSize * fontSizeFactor + fontSizeDelta, fontWeight: fontWeight == null ? null : FontWeight.values[(fontWeight.index + fontWeightDelta).clamp(0, FontWeight.values.length - 1)], fontStyle: fontStyle, @@ -556,6 +598,7 @@ class TextStyle extends Diagnosticable { return copyWith( color: other.color, fontFamily: other.fontFamily, + fontFamilyFallback: other.fontFamilyFallback, fontSize: other.fontSize, fontWeight: other.fontWeight, fontStyle: other.fontStyle, @@ -601,6 +644,7 @@ class TextStyle extends Diagnosticable { inherit: b.inherit, color: Color.lerp(null, b.color, t), fontFamily: t < 0.5 ? null : b.fontFamily, + fontFamilyFallback: t < 0.5 ? null : b.fontFamilyFallback, fontSize: t < 0.5 ? null : b.fontSize, fontWeight: FontWeight.lerp(null, b.fontWeight, t), fontStyle: t < 0.5 ? null : b.fontStyle, @@ -624,6 +668,7 @@ class TextStyle extends Diagnosticable { inherit: a.inherit, color: Color.lerp(a.color, null, t), fontFamily: t < 0.5 ? a.fontFamily : null, + fontFamilyFallback: t < 0.5 ? a.fontFamilyFallback : null, fontSize: t < 0.5 ? a.fontSize : null, fontWeight: FontWeight.lerp(a.fontWeight, null, t), fontStyle: t < 0.5 ? a.fontStyle : null, @@ -646,6 +691,7 @@ class TextStyle extends Diagnosticable { inherit: b.inherit, color: a.foreground == null && b.foreground == null ? Color.lerp(a.color, b.color, t) : null, fontFamily: t < 0.5 ? a.fontFamily : b.fontFamily, + fontFamilyFallback: t < 0.5 ? a.fontFamilyFallback : b.fontFamilyFallback, fontSize: ui.lerpDouble(a.fontSize ?? b.fontSize, b.fontSize ?? a.fontSize, t), fontWeight: FontWeight.lerp(a.fontWeight, b.fontWeight, t), fontStyle: t < 0.5 ? a.fontStyle : b.fontStyle, @@ -679,6 +725,7 @@ class TextStyle extends Diagnosticable { fontStyle: fontStyle, textBaseline: textBaseline, fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, fontSize: fontSize == null ? null : fontSize * textScaleFactor, letterSpacing: letterSpacing, wordSpacing: wordSpacing, @@ -743,7 +790,8 @@ class TextStyle extends Diagnosticable { locale != other.locale || foreground != other.foreground || background != other.background || - !listEquals(shadows, other.shadows)) + !listEquals(shadows, other.shadows) || + !listEquals(fontFamilyFallback, other.fontFamilyFallback)) return RenderComparison.layout; if (color != other.color || decoration != other.decoration || @@ -776,7 +824,8 @@ class TextStyle extends Diagnosticable { decoration == typedOther.decoration && decorationColor == typedOther.decorationColor && decorationStyle == typedOther.decorationStyle && - listEquals(shadows, typedOther.shadows); + listEquals(shadows, typedOther.shadows) && + listEquals(fontFamilyFallback, typedOther.fontFamilyFallback); } @override @@ -785,6 +834,7 @@ class TextStyle extends Diagnosticable { inherit, color, fontFamily, + fontFamilyFallback, fontSize, fontWeight, fontStyle, @@ -814,6 +864,7 @@ class TextStyle extends Diagnosticable { final List styles = []; styles.add(DiagnosticsProperty('${prefix}color', color, defaultValue: null)); styles.add(StringProperty('${prefix}family', fontFamily, defaultValue: null, quoted: false)); + styles.add(IterableProperty('${prefix}familyFallback', fontFamilyFallback, defaultValue: null)); styles.add(DoubleProperty('${prefix}size', fontSize, defaultValue: null)); String weightDescription; if (fontWeight != null) { diff --git a/packages/flutter/test/material/theme_test.dart b/packages/flutter/test/material/theme_test.dart index de24c86aa0..474a0322eb 100644 --- a/packages/flutter/test/material/theme_test.dart +++ b/packages/flutter/test/material/theme_test.dart @@ -671,6 +671,7 @@ class _TextStyleProxy implements TextStyle { @override Color get decorationColor => _delegate.decorationColor; @override TextDecorationStyle get decorationStyle => _delegate.decorationStyle; @override String get fontFamily => _delegate.fontFamily; + @override List get fontFamilyFallback => _delegate.fontFamilyFallback; @override double get fontSize => _delegate.fontSize; @override FontStyle get fontStyle => _delegate.fontStyle; @override FontWeight get fontWeight => _delegate.fontWeight; @@ -699,7 +700,7 @@ class _TextStyleProxy implements TextStyle { } @override - TextStyle apply({Color color, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, String fontFamily, double fontSizeFactor = 1.0, double fontSizeDelta = 0.0, int fontWeightDelta = 0, double letterSpacingFactor = 1.0, double letterSpacingDelta = 0.0, double wordSpacingFactor = 1.0, double wordSpacingDelta = 0.0, double heightFactor = 1.0, double heightDelta = 0.0}) { + TextStyle apply({Color color, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, String fontFamily, List fontFamilyFallback, double fontSizeFactor = 1.0, double fontSizeDelta = 0.0, int fontWeightDelta = 0, double letterSpacingFactor = 1.0, double letterSpacingDelta = 0.0, double wordSpacingFactor = 1.0, double wordSpacingDelta = 0.0, double heightFactor = 1.0, double heightDelta = 0.0}) { throw UnimplementedError(); } @@ -709,7 +710,7 @@ class _TextStyleProxy implements TextStyle { } @override - TextStyle copyWith({Color color, String fontFamily, double fontSize, FontWeight fontWeight, FontStyle fontStyle, double letterSpacing, double wordSpacing, TextBaseline textBaseline, double height, Locale locale, ui.Paint foreground, ui.Paint background, List shadows, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, String debugLabel}) { + TextStyle copyWith({Color color, String fontFamily, List fontFamilyFallback, double fontSize, FontWeight fontWeight, FontStyle fontStyle, double letterSpacing, double wordSpacing, TextBaseline textBaseline, double height, Locale locale, ui.Paint foreground, ui.Paint background, List shadows, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, String debugLabel}) { throw UnimplementedError(); } diff --git a/packages/flutter/test/painting/text_style_test.dart b/packages/flutter/test/painting/text_style_test.dart index b26064452d..4d65194e99 100644 --- a/packages/flutter/test/painting/text_style_test.dart +++ b/packages/flutter/test/painting/text_style_test.dart @@ -163,10 +163,10 @@ void main() { final ui.TextStyle ts5 = s5.getTextStyle(); expect(ts5, equals(ui.TextStyle(fontWeight: FontWeight.w700, fontSize: 12.0, height: 123.0))); - expect(ts5.toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontSize: 12.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 123.0x, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); + expect(ts5.toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 12.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 123.0x, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); final ui.TextStyle ts2 = s2.getTextStyle(); expect(ts2, equals(ui.TextStyle(color: const Color(0xFF00FF00), fontWeight: FontWeight.w800, fontSize: 10.0, height: 100.0))); - expect(ts2.toString(), 'TextStyle(color: Color(0xff00ff00), decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontSize: 10.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 100.0x, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); + expect(ts2.toString(), 'TextStyle(color: Color(0xff00ff00), decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 10.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 100.0x, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); final ui.ParagraphStyle ps2 = s2.getParagraphStyle(textAlign: TextAlign.center); expect(ps2, equals(ui.ParagraphStyle(textAlign: TextAlign.center, fontWeight: FontWeight.w800, fontSize: 10.0, lineHeight: 100.0))); @@ -176,6 +176,7 @@ void main() { expect(ps5.toString(), 'ParagraphStyle(textAlign: unspecified, textDirection: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: 12.0, lineHeight: 123.0x, ellipsis: unspecified, locale: unspecified)'); }); + test('TextStyle with text direction', () { final ui.ParagraphStyle ps6 = const TextStyle().getParagraphStyle(textDirection: TextDirection.ltr); expect(ps6, equals(ui.ParagraphStyle(textDirection: TextDirection.ltr, fontSize: 14.0))); @@ -189,11 +190,47 @@ void main() { test('TextStyle using package font', () { const TextStyle s6 = TextStyle(fontFamily: 'test'); expect(s6.fontFamily, 'test'); - expect(s6.getTextStyle().toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: test, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); + expect(s6.getTextStyle().toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: test, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); const TextStyle s7 = TextStyle(fontFamily: 'test', package: 'p'); expect(s7.fontFamily, 'packages/p/test'); - expect(s7.getTextStyle().toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: packages/p/test, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); + expect(s7.getTextStyle().toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: packages/p/test, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); + + const TextStyle s8 = TextStyle(fontFamilyFallback: ['test', 'test2'], package: 'p'); + expect(s8.fontFamilyFallback[0], 'packages/p/test'); + expect(s8.fontFamilyFallback[1], 'packages/p/test2'); + expect(s8.fontFamilyFallback.length, 2); + + const TextStyle s9 = TextStyle(package: 'p'); + expect(s9.fontFamilyFallback, null); + + const TextStyle s10 = TextStyle(fontFamilyFallback: [], package: 'p'); + expect(s10.fontFamilyFallback, []); + }); + + test('TextStyle font family fallback', () { + const TextStyle s1 = TextStyle(fontFamilyFallback: ['Roboto', 'test']); + expect(s1.fontFamilyFallback[0], 'Roboto'); + expect(s1.fontFamilyFallback[1], 'test'); + expect(s1.fontFamilyFallback.length, 2); + + const TextStyle s2 = TextStyle(fontFamily: 'foo', fontFamilyFallback: ['Roboto', 'test']); + expect(s2.fontFamilyFallback[0], 'Roboto'); + expect(s2.fontFamilyFallback[1], 'test'); + expect(s2.fontFamily, 'foo'); + expect(s2.fontFamilyFallback.length, 2); + + const TextStyle s3 = TextStyle(fontFamily: 'foo'); + expect(s3.fontFamily, 'foo'); + expect(s3.fontFamilyFallback, null); + + const TextStyle s4 = TextStyle(fontFamily: 'foo', fontFamilyFallback: []); + expect(s4.fontFamily, 'foo'); + expect(s4.fontFamilyFallback, []); + expect(s4.fontFamilyFallback.isEmpty, true); + + final ui.TextStyle uis1 = s2.getTextStyle(); + expect(uis1.toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: foo, fontFamilyFallback: [Roboto, test], fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); }); test('TextStyle.debugLabel', () {