@@ -14,8 +14,8 @@ const String _testFontUrl = 'packages/ui/assets/ahem.ttf';
1414/// font manifest. If test fonts are enabled, then call
1515/// [registerTestFonts] as well.
1616class FontCollection {
17- _FontManager _assetFontManager;
18- _FontManager _testFontManager;
17+ FontManager _assetFontManager;
18+ FontManager _testFontManager;
1919
2020 /// Reads the font manifest using the [assetManager] and registers all of the
2121 /// fonts declared within.
@@ -47,7 +47,7 @@ class FontCollection {
4747 }
4848
4949 if (supportsFontLoadingApi) {
50- _assetFontManager = _FontManager ();
50+ _assetFontManager = FontManager ();
5151 } else {
5252 _assetFontManager = _PolyfillFontManager ();
5353 }
@@ -73,7 +73,7 @@ class FontCollection {
7373
7474 /// Registers fonts that are used by tests.
7575 void debugRegisterTestFonts () {
76- _testFontManager = _FontManager ();
76+ _testFontManager = FontManager ();
7777 _testFontManager.registerAsset (
7878 _testFontFamily, 'url($_testFontUrl )' , const < String , String > {});
7979 }
@@ -96,40 +96,78 @@ class FontCollection {
9696}
9797
9898/// Manages a collection of fonts and ensures they are loaded.
99- class _FontManager {
99+ class FontManager {
100100 final List <Future <void >> _fontLoadingFutures = < Future <void >> [];
101101
102- factory _FontManager () {
102+ factory FontManager () {
103103 if (supportsFontLoadingApi) {
104- return _FontManager ._();
104+ return FontManager ._();
105105 } else {
106106 return _PolyfillFontManager ();
107107 }
108108 }
109109
110- _FontManager ._();
111-
110+ FontManager ._();
111+
112+ /// Registers assets to Flutter Web Engine.
113+ ///
114+ /// Browsers and browesers versions differ siginificantly on how a valid font
115+ /// family name should be formatted. Notable issues are:
116+ /// Safari12 and Firefox crash if you create a [html.FontFace] with a font
117+ /// family that is not correct CSS syntax. Font family names accepted on these
118+ /// browsers, when wrapped it in quotes.
119+ /// Additionally, for Safari12 to work [html.FontFace] name should be loaded
120+ /// correctly on the first try.
121+ /// A font in Chrome chrashes if a [html.FontFace] is loaded only with quotes.
122+ /// Unlike Safari12 if a valid version is loaded afterwards it will show
123+ /// that fonts normally.
124+ /// In Safari 13 the [html.FontFace] should be loaded with unquoted family
125+ /// names.
126+ /// In order to avoid all these browser compatibility issues this method;
127+ /// detects the family names that might cause a conflict, loads it with
128+ /// quotes and loads it again without quotes. For all the other family names
129+ /// [html.FontFace] is loaded only once.
130+ /// See: https://developer.mozilla.org/en-US/docs/Web/CSS/font-family#Valid_family_names
131+ /// See: https://drafts.csswg.org/css-fonts-3/#font-family-prop
112132 void registerAsset (
113133 String family,
114134 String asset,
115135 Map <String , String > descriptors,
116136 ) {
117- // Safari and Firefox crash if you create a [html.FontFace] with a font
118- // family that is not correct CSS syntax. To ensure the font family is
119- // accepted on these browsers, wrap it in quotes.
120- // See: https://drafts.csswg.org/css-fonts-3/#font-family-prop
121- if (browserEngine == BrowserEngine .firefox) {
122- family = "'$family '" ;
137+ final String familyNameInQuotes = "'$family '" ;
138+ // Regular expression to detect punctuations. For example font family
139+ // 'Ahem!' falls into this category.
140+ final RegExp punctuations = RegExp (r"[.,:;!`\/#\$\%\^&~\*=\-_(){}]" );
141+ // Regular expression to detect tokens starting with a digit.
142+ // For example font family 'Goudy Bookletter 1911' falls into this
143+ // category.
144+ final RegExp startWithDigit = RegExp (r"\b\d" );
145+ // Fonts names when a package dependency is added has '/' in the family
146+ // names such as 'package/material_design_icons_flutter/...'
147+ if (family.contains ('/' ) ||
148+ punctuations.hasMatch (family) ||
149+ startWithDigit.hasMatch (family)) {
150+ // Load a font family name with special chracters once here wrapped in
151+ // quotes.
152+ _loadFontFace (familyNameInQuotes, asset, descriptors);
123153 }
154+ // Load all font fonts, without quoted family names.
155+ _loadFontFace (family, asset, descriptors);
156+ }
157+
158+ void _loadFontFace (
159+ String family,
160+ String asset,
161+ Map <String , String > descriptors,
162+ ) {
124163 // try/catch because `new FontFace` can crash with an improper font family.
125164 try {
126165 final html.FontFace fontFace = html.FontFace (family, asset, descriptors);
127- _fontLoadingFutures.add (fontFace
128- . load ()
129- . then ((_) => html.document.fonts. add (fontFace) , onError: (dynamic e) {
166+ _fontLoadingFutures.add (fontFace. load (). then ((_) {
167+ html.document.fonts. add (fontFace);
168+ } , onError: (dynamic e) {
130169 html.window.console
131170 .warn ('Error while trying to load font family "$family ":\n $e ' );
132- return null ;
133171 }));
134172 } catch (e) {
135173 html.window.console
@@ -149,7 +187,7 @@ class _FontManager {
149187/// The CSS Font Loading API is not implemented in IE 11 or Edge. To tell if a
150188/// font is loaded, we continuously measure some text using that font until the
151189/// width changes.
152- class _PolyfillFontManager extends _FontManager {
190+ class _PolyfillFontManager extends FontManager {
153191 _PolyfillFontManager () : super ._();
154192
155193 /// A String containing characters whose width varies greatly between fonts.
0 commit comments