22// Use of this source code is governed by a BSD-style license that can be
33// found in the LICENSE file.
44
5+ import 'dart:async' ;
56import 'dart:convert' ;
67import 'dart:typed_data' ;
78
@@ -22,44 +23,37 @@ const String _robotoUrl =
2223
2324/// Manages the fonts used in the Skia-based backend.
2425class SkiaFontCollection implements FontCollection {
25- final Set <String > _registeredFontFamilies = < String > {};
26+ final Set <String > _downloadedFontFamilies = < String > {};
2627
27- /// Fonts that started the download process.
28+ /// Fonts that started the download process, but are not yet registered .
2829 ///
29- /// Once downloaded successfully, this map is cleared and the resulting
30- /// [RegisteredFont ] s are added to [_downloadedFonts ] .
31- final List <Future < RegisteredFont ?>> _pendingFonts = < Future < RegisteredFont ?> > [];
30+ /// /// Once downloaded successfully, this map is cleared and the resulting
31+ /// [UnregisteredFont ] s are added to [_registeredFonts ] .
32+ final List <UnregisteredFont > _unregisteredFonts = < UnregisteredFont > [];
3233
33- /// Fonts that have been downloaded and parsed into [SkTypeface] .
34- ///
35- /// These fonts may not yet have been registered with the [fontProvider] . This
36- /// happens after [ensureFontsLoaded] completes.
37- final List <RegisteredFont > _downloadedFonts = < RegisteredFont > [];
34+ final List <RegisteredFont > _registeredFonts = < RegisteredFont > [];
3835
39- /// Returns fonts that have been downloaded and parsed.
36+ /// Returns fonts that have been downloaded, registered, and parsed.
4037 ///
4138 /// This should only be used in tests.
42- List <RegisteredFont >? get debugDownloadedFonts {
39+ List <RegisteredFont >? get debugRegisteredFonts {
4340 if (! assertionsEnabled) {
4441 return null ;
4542 }
46- return _downloadedFonts ;
43+ return _registeredFonts ;
4744 }
4845
4946 final Map <String , List <SkFont >> familyToFontMap = < String , List <SkFont >> {};
5047
51- @override
52- Future <void > ensureFontsLoaded () async {
53- await _loadFonts ();
54-
48+ void _registerWithFontProvider () {
5549 if (fontProvider != null ) {
5650 fontProvider! .delete ();
5751 fontProvider = null ;
5852 }
5953 fontProvider = canvasKit.TypefaceFontProvider .Make ();
6054 familyToFontMap.clear ();
6155
62- for (final RegisteredFont font in _downloadedFonts ) {
56+ for (final RegisteredFont font in _registeredFonts ) {
6357 fontProvider! .registerFont (font.bytes, font.family);
6458 familyToFontMap
6559 .putIfAbsent (font.family, () => < SkFont > [])
@@ -75,21 +69,6 @@ class SkiaFontCollection implements FontCollection {
7569 }
7670 }
7771
78- /// Loads all of the unloaded fonts in [_pendingFonts] and adds them
79- /// to [_downloadedFonts] .
80- Future <void > _loadFonts () async {
81- if (_pendingFonts.isEmpty) {
82- return ;
83- }
84- final List <RegisteredFont ?> loadedFonts = await Future .wait (_pendingFonts);
85- for (final RegisteredFont ? font in loadedFonts) {
86- if (font != null ) {
87- _downloadedFonts.add (font);
88- }
89- }
90- _pendingFonts.clear ();
91- }
92-
9372 @override
9473 Future <void > loadFontFromList (Uint8List list, {String ? fontFamily}) async {
9574 if (fontFamily == null ) {
@@ -103,8 +82,8 @@ class SkiaFontCollection implements FontCollection {
10382 final SkTypeface ? typeface =
10483 canvasKit.Typeface .MakeFreeTypeFaceFromData (list.buffer);
10584 if (typeface != null ) {
106- _downloadedFonts .add (RegisteredFont (list, fontFamily, typeface));
107- await ensureFontsLoaded ();
85+ _registeredFonts .add (RegisteredFont (list, fontFamily, typeface));
86+ _registerWithFontProvider ();
10887 } else {
10988 printWarning ('Failed to parse font family "$fontFamily "' );
11089 return ;
@@ -113,7 +92,7 @@ class SkiaFontCollection implements FontCollection {
11392
11493 /// Loads fonts from `FontManifest.json` .
11594 @override
116- Future <void > registerFonts (AssetManager assetManager) async {
95+ Future <void > downloadAssetFonts (AssetManager assetManager) async {
11796 ByteData byteData;
11897
11998 try {
@@ -134,79 +113,112 @@ class SkiaFontCollection implements FontCollection {
134113 'There was a problem trying to load FontManifest.json' );
135114 }
136115
116+ final List <Future <UnregisteredFont ?>> pendingFonts = < Future <UnregisteredFont ?>> [];
117+
137118 for (final Map <String , dynamic > fontFamily
138119 in fontManifest.cast <Map <String , dynamic >>()) {
139120 final String family = fontFamily.readString ('family' );
140121 final List <dynamic > fontAssets = fontFamily.readList ('fonts' );
141122 for (final dynamic fontAssetItem in fontAssets) {
142123 final Map <String , dynamic > fontAsset = fontAssetItem as Map <String , dynamic >;
143124 final String asset = fontAsset.readString ('asset' );
144- _registerFont ( assetManager.getAssetUrl (asset), family);
125+ _downloadFont (pendingFonts, assetManager.getAssetUrl (asset), family);
145126 }
146127 }
147128
148129 /// We need a default fallback font for CanvasKit, in order to
149130 /// avoid crashing while laying out text with an unregistered font. We chose
150131 /// Roboto to match Android.
151- if (! _isFontFamilyRegistered ('Roboto' )) {
132+ if (! _isFontFamilyDownloaded ('Roboto' )) {
152133 // Download Roboto and add it to the font buffers.
153- _registerFont (_robotoUrl, 'Roboto' );
134+ _downloadFont (pendingFonts, _robotoUrl, 'Roboto' );
135+ }
136+
137+ final List <UnregisteredFont ?> completedPendingFonts = await Future .wait (pendingFonts);
138+ _unregisteredFonts.addAll (completedPendingFonts.whereType <UnregisteredFont >());
139+ }
140+
141+ @override
142+ void registerDownloadedFonts () {
143+ RegisteredFont ? makeRegisterFont (ByteBuffer buffer, String url, String family) {
144+ final Uint8List bytes = buffer.asUint8List ();
145+ final SkTypeface ? typeface =
146+ canvasKit.Typeface .MakeFreeTypeFaceFromData (bytes.buffer);
147+ if (typeface != null ) {
148+ return RegisteredFont (bytes, family, typeface);
149+ } else {
150+ printWarning ('Failed to load font $family at $url ' );
151+ printWarning ('Verify that $url contains a valid font.' );
152+ return null ;
153+ }
154+ }
155+
156+ for (final UnregisteredFont unregisteredFont in _unregisteredFonts) {
157+ final RegisteredFont ? registeredFont = makeRegisterFont (
158+ unregisteredFont.bytes,
159+ unregisteredFont.url,
160+ unregisteredFont.family
161+ );
162+ if (registeredFont != null ) {
163+ _registeredFonts.add (registeredFont);
164+ }
154165 }
166+
167+ _unregisteredFonts.clear ();
168+ _registerWithFontProvider ();
155169 }
156170
157171 /// Whether the [fontFamily] was registered and/or loaded.
158- bool _isFontFamilyRegistered (String fontFamily) {
159- return _registeredFontFamilies .contains (fontFamily);
172+ bool _isFontFamilyDownloaded (String fontFamily) {
173+ return _downloadedFontFamilies .contains (fontFamily);
160174 }
161175
162176 /// Loads the Ahem font, unless it's already been loaded using
163- /// `FontManifest.json` (see [registerFonts ] ).
177+ /// `FontManifest.json` (see [downloadAssetFonts ] ).
164178 ///
165179 /// `FontManifest.json` has higher priority than the default test font URLs.
166180 /// This allows customizing test environments where fonts are loaded from
167181 /// different URLs.
168182 @override
169- void debugRegisterTestFonts () {
170- if (! _isFontFamilyRegistered (ahemFontFamily)) {
171- _registerFont (ahemFontUrl, ahemFontFamily);
183+ Future <void > debugDownloadTestFonts () async {
184+ final List <Future <UnregisteredFont ?>> pendingFonts = < Future <UnregisteredFont ?>> [];
185+ if (! _isFontFamilyDownloaded (ahemFontFamily)) {
186+ _downloadFont (pendingFonts, ahemFontUrl, ahemFontFamily);
172187 }
173- if (! _isFontFamilyRegistered (robotoFontFamily)) {
174- _registerFont ( robotoTestFontUrl, robotoFontFamily);
188+ if (! _isFontFamilyDownloaded (robotoFontFamily)) {
189+ _downloadFont (pendingFonts, robotoTestFontUrl, robotoFontFamily);
175190 }
176- if (! _isFontFamilyRegistered (robotoVariableFontFamily)) {
177- _registerFont ( robotoVariableTestFontUrl, robotoVariableFontFamily);
191+ if (! _isFontFamilyDownloaded (robotoVariableFontFamily)) {
192+ _downloadFont (pendingFonts, robotoVariableTestFontUrl, robotoVariableFontFamily);
178193 }
179194
195+ final List <UnregisteredFont ?> completedPendingFonts = await Future .wait (pendingFonts);
196+ _unregisteredFonts.addAll (completedPendingFonts.whereType <UnregisteredFont >());
197+
180198 // Ahem must be added to font fallbacks list regardless of where it was
181199 // downloaded from.
182200 FontFallbackData .instance.globalFontFallbacks.add (ahemFontFamily);
183201 }
184202
185- void _registerFont (String url, String family) {
186- Future <RegisteredFont ?> downloadFont () async {
203+ void _downloadFont (
204+ List <Future <UnregisteredFont ?>> waitUnregisteredFonts,
205+ String url,
206+ String family
207+ ) {
208+ Future <UnregisteredFont ?> downloadFont () async {
187209 ByteBuffer buffer;
188210 try {
189211 buffer = await httpFetch (url).then (_getArrayBuffer);
212+ return UnregisteredFont (buffer, url, family);
190213 } catch (e) {
191214 printWarning ('Failed to load font $family at $url ' );
192215 printWarning (e.toString ());
193216 return null ;
194217 }
195-
196- final Uint8List bytes = buffer.asUint8List ();
197- final SkTypeface ? typeface =
198- canvasKit.Typeface .MakeFreeTypeFaceFromData (bytes.buffer);
199- if (typeface != null ) {
200- return RegisteredFont (bytes, family, typeface);
201- } else {
202- printWarning ('Failed to load font $family at $url ' );
203- printWarning ('Verify that $url contains a valid font.' );
204- return null ;
205- }
206218 }
207219
208- _registeredFontFamilies .add (family);
209- _pendingFonts .add (downloadFont ());
220+ _downloadedFontFamilies .add (family);
221+ waitUnregisteredFonts .add (downloadFont ());
210222 }
211223
212224
@@ -249,3 +261,11 @@ class RegisteredFont {
249261 /// This is used to determine which code points are supported by this font.
250262 final SkTypeface typeface;
251263}
264+
265+ /// Represents a font that has been downloaded but not registered.
266+ class UnregisteredFont {
267+ const UnregisteredFont (this .bytes, this .url, this .family);
268+ final ByteBuffer bytes;
269+ final String url;
270+ final String family;
271+ }
0 commit comments