@@ -16,8 +16,8 @@ const String _robotoFontUrl = 'packages/ui/assets/Roboto-Regular.ttf';
1616/// font manifest. If test fonts are enabled, then call
1717/// [registerTestFonts] as well.
1818class FontCollection {
19- _FontManager _assetFontManager;
20- _FontManager _testFontManager;
19+ FontManager _assetFontManager;
20+ FontManager _testFontManager;
2121
2222 /// Reads the font manifest using the [assetManager] and registers all of the
2323 /// fonts declared within.
@@ -49,7 +49,7 @@ class FontCollection {
4949 }
5050
5151 if (supportsFontLoadingApi) {
52- _assetFontManager = _FontManager ();
52+ _assetFontManager = FontManager ();
5353 } else {
5454 _assetFontManager = _PolyfillFontManager ();
5555 }
@@ -75,7 +75,7 @@ class FontCollection {
7575
7676 /// Registers fonts that are used by tests.
7777 void debugRegisterTestFonts () {
78- _testFontManager = _FontManager ();
78+ _testFontManager = FontManager ();
7979 _testFontManager.registerAsset (
8080 _ahemFontFamily, 'url($_ahemFontUrl )' , const < String , String > {});
8181 _testFontManager.registerAsset (
@@ -100,40 +100,78 @@ class FontCollection {
100100}
101101
102102/// Manages a collection of fonts and ensures they are loaded.
103- class _FontManager {
103+ class FontManager {
104104 final List <Future <void >> _fontLoadingFutures = < Future <void >> [];
105105
106- factory _FontManager () {
106+ factory FontManager () {
107107 if (supportsFontLoadingApi) {
108- return _FontManager ._();
108+ return FontManager ._();
109109 } else {
110110 return _PolyfillFontManager ();
111111 }
112112 }
113113
114- _FontManager ._();
115-
114+ FontManager ._();
115+
116+ /// Registers assets to Flutter Web Engine.
117+ ///
118+ /// Browsers and browesers versions differ siginificantly on how a valid font
119+ /// family name should be formatted. Notable issues are:
120+ /// Safari12 and Firefox crash if you create a [html.FontFace] with a font
121+ /// family that is not correct CSS syntax. Font family names accepted on these
122+ /// browsers, when wrapped it in quotes.
123+ /// Additionally, for Safari12 to work [html.FontFace] name should be loaded
124+ /// correctly on the first try.
125+ /// A font in Chrome chrashes if a [html.FontFace] is loaded only with quotes.
126+ /// Unlike Safari12 if a valid version is loaded afterwards it will show
127+ /// that fonts normally.
128+ /// In Safari 13 the [html.FontFace] should be loaded with unquoted family
129+ /// names.
130+ /// In order to avoid all these browser compatibility issues this method;
131+ /// detects the family names that might cause a conflict, loads it with
132+ /// quotes and loads it again without quotes. For all the other family names
133+ /// [html.FontFace] is loaded only once.
134+ /// See: https://developer.mozilla.org/en-US/docs/Web/CSS/font-family#Valid_family_names
135+ /// See: https://drafts.csswg.org/css-fonts-3/#font-family-prop
116136 void registerAsset (
117137 String family,
118138 String asset,
119139 Map <String , String > descriptors,
120140 ) {
121- // Safari and Firefox crash if you create a [html.FontFace] with a font
122- // family that is not correct CSS syntax. To ensure the font family is
123- // accepted on these browsers, wrap it in quotes.
124- // See: https://drafts.csswg.org/css-fonts-3/#font-family-prop
125- if (browserEngine == BrowserEngine .firefox) {
126- family = "'$family '" ;
141+ final String familyNameInQuotes = "'$family '" ;
142+ // Regular expression to detect punctuations. For example font family
143+ // 'Ahem!' falls into this category.
144+ final RegExp punctuations = RegExp (r"[.,:;!`\/#\$\%\^&~\*=\-_(){}]" );
145+ // Regular expression to detect tokens starting with a digit.
146+ // For example font family 'Goudy Bookletter 1911' falls into this
147+ // category.
148+ final RegExp startWithDigit = RegExp (r"\b\d" );
149+ // Fonts names when a package dependency is added has '/' in the family
150+ // names such as 'package/material_design_icons_flutter/...'
151+ if (family.contains ('/' ) ||
152+ punctuations.hasMatch (family) ||
153+ startWithDigit.hasMatch (family)) {
154+ // Load a font family name with special chracters once here wrapped in
155+ // quotes.
156+ _loadFontFace (familyNameInQuotes, asset, descriptors);
127157 }
158+ // Load all font fonts, without quoted family names.
159+ _loadFontFace (family, asset, descriptors);
160+ }
161+
162+ void _loadFontFace (
163+ String family,
164+ String asset,
165+ Map <String , String > descriptors,
166+ ) {
128167 // try/catch because `new FontFace` can crash with an improper font family.
129168 try {
130169 final html.FontFace fontFace = html.FontFace (family, asset, descriptors);
131- _fontLoadingFutures.add (fontFace
132- . load ()
133- . then ((_) => html.document.fonts. add (fontFace) , onError: (dynamic e) {
170+ _fontLoadingFutures.add (fontFace. load (). then ((_) {
171+ html.document.fonts. add (fontFace);
172+ } , onError: (dynamic e) {
134173 html.window.console
135174 .warn ('Error while trying to load font family "$family ":\n $e ' );
136- return null ;
137175 }));
138176 } catch (e) {
139177 html.window.console
@@ -153,7 +191,7 @@ class _FontManager {
153191/// The CSS Font Loading API is not implemented in IE 11 or Edge. To tell if a
154192/// font is loaded, we continuously measure some text using that font until the
155193/// width changes.
156- class _PolyfillFontManager extends _FontManager {
194+ class _PolyfillFontManager extends FontManager {
157195 _PolyfillFontManager () : super ._();
158196
159197 /// A String containing characters whose width varies greatly between fonts.
0 commit comments