Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 7406fd8

Browse files
author
Harry Terkelsen
authored
[reland] Canvaskit get googlefont data (#35646)
1 parent f10ffb4 commit 7406fd8

14 files changed

Lines changed: 820 additions & 646 deletions

ci/licenses_golden/licenses_flutter

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart
11401140
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart
11411141
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart
11421142
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/embedded_views_diff.dart
1143+
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/font_fallback_data.dart
11431144
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart
11441145
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/fonts.dart
11451146
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/image.dart
@@ -1152,6 +1153,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.d
11521153
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart
11531154
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart
11541155
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/n_way_canvas.dart
1156+
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/noto_font.dart
11551157
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/painting.dart
11561158
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/path.dart
11571159
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/path_metrics.dart

lib/web_ui/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,19 @@ directly), follow these steps to roll to the new version:
205205
If you have questions, contact the Flutter Web team on Flutter Discord on the
206206
#hackers-web-🌍 channel.
207207
208+
### Rolling Noto Font Data
209+
210+
In order to generate new data for the Noto fallback fonts, you will need
211+
a GoogleFonts API key. Once you have one, run:
212+
213+
```
214+
./dev/felt generate-fallback-font-data --key=<your GoogleFonts API key>
215+
```
216+
217+
This will generate the file `lib/src/engine/canvaskit/font_fallback_data.dart` with
218+
the latest data from GoogleFonts. This generated file should then be rolled in with
219+
a PR to the engine.
220+
208221
### Configuration files
209222

210223
`browser_lock.yaml` contains the version of browsers we use to test Flutter for

lib/web_ui/dev/felt.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'package:args/command_runner.dart';
99
import 'build.dart';
1010
import 'clean.dart';
1111
import 'exceptions.dart';
12+
import 'generate_fallback_font_data.dart';
1213
import 'licenses.dart';
1314
import 'run.dart';
1415
import 'test_runner.dart';
@@ -20,6 +21,7 @@ CommandRunner<bool> runner = CommandRunner<bool>(
2021
)
2122
..addCommand(BuildCommand())
2223
..addCommand(CleanCommand())
24+
..addCommand(GenerateFallbackFontDataCommand())
2325
..addCommand(LicensesCommand())
2426
..addCommand(RunCommand())
2527
..addCommand(TestCommand());
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:convert' show jsonDecode;
6+
import 'dart:io' as io;
7+
8+
import 'package:args/command_runner.dart';
9+
import 'package:http/http.dart' as http;
10+
import 'package:path/path.dart' as path;
11+
12+
import 'environment.dart';
13+
import 'exceptions.dart';
14+
import 'utils.dart';
15+
16+
class GenerateFallbackFontDataCommand extends Command<bool>
17+
with ArgUtils<bool> {
18+
GenerateFallbackFontDataCommand() {
19+
argParser.addOption(
20+
'key',
21+
defaultsTo: '',
22+
help: 'The Google Fonts API key. Used to get data about fonts hosted on '
23+
'Google Fonts.',
24+
);
25+
argParser.addFlag(
26+
'download-test-fonts',
27+
defaultsTo: true,
28+
help: 'Whether to download the Noto fonts into a local folder to use in'
29+
'tests.',
30+
);
31+
}
32+
33+
@override
34+
final String name = 'generate-fallback-font-data';
35+
36+
@override
37+
final String description = 'Generate fallback font data from GoogleFonts';
38+
39+
String get apiKey => stringArg('key');
40+
41+
bool get downloadTestFonts => boolArg('download-test-fonts');
42+
43+
@override
44+
Future<bool> run() async {
45+
await _generateFallbackFontData();
46+
return true;
47+
}
48+
49+
Future<void> _generateFallbackFontData() async {
50+
if (apiKey.isEmpty) {
51+
throw UsageException('No Google Fonts API key provided', argParser.usage);
52+
}
53+
final http.Client client = http.Client();
54+
final http.Response response = await client.get(Uri.parse(
55+
'https://www.googleapis.com/webfonts/v1/webfonts?key=$apiKey'));
56+
if (response.statusCode != 200) {
57+
throw ToolExit('Failed to download Google Fonts list.');
58+
}
59+
final Map<String, dynamic> googleFontsResult =
60+
jsonDecode(response.body) as Map<String, dynamic>;
61+
final List<Map<String, dynamic>> fontDatas =
62+
(googleFontsResult['items'] as List<dynamic>)
63+
.cast<Map<String, dynamic>>();
64+
final Map<String, Uri> urlForFamily = <String, Uri>{};
65+
for (final Map<String, dynamic> fontData in fontDatas) {
66+
if (fallbackFonts.contains(fontData['family'])) {
67+
final Uri uri = Uri.parse(fontData['files']['regular'] as String);
68+
urlForFamily[fontData['family'] as String] = uri;
69+
}
70+
}
71+
final Map<String, String> charsetForFamily = <String, String>{};
72+
final io.Directory fontDir = downloadTestFonts
73+
? await io.Directory(path.join(
74+
environment.webUiBuildDir.path,
75+
'assets',
76+
'noto',
77+
)).create(recursive: true)
78+
: await io.Directory.systemTemp.createTemp('fonts');
79+
// Delete old fonts in the font directory.
80+
await for (final io.FileSystemEntity file in fontDir.list()) {
81+
await file.delete();
82+
}
83+
for (final String family in fallbackFonts) {
84+
print('Downloading $family...');
85+
final Uri uri = urlForFamily[family]!;
86+
final http.Response fontResponse = await client.get(uri);
87+
if (fontResponse.statusCode != 200) {
88+
throw ToolExit('Failed to download font for $family');
89+
}
90+
final io.File fontFile =
91+
io.File(path.join(fontDir.path, path.basename(uri.path)));
92+
await fontFile.writeAsBytes(fontResponse.bodyBytes);
93+
final io.ProcessResult fcQueryResult =
94+
await io.Process.run('fc-query', <String>[
95+
'--format=%{charset}',
96+
'--',
97+
fontFile.path,
98+
]);
99+
final String encodedCharset = fcQueryResult.stdout as String;
100+
charsetForFamily[family] = encodedCharset;
101+
}
102+
103+
final StringBuffer sb = StringBuffer();
104+
105+
sb.writeln('// Copyright 2013 The Flutter Authors. All rights reserved.');
106+
sb.writeln('// Use of this source code is governed by a BSD-style license '
107+
'that can be');
108+
sb.writeln('// found in the LICENSE file.');
109+
sb.writeln();
110+
sb.writeln('// DO NOT EDIT! This file is generated. See:');
111+
sb.writeln('// dev/generate_fallback_font_data.dart');
112+
sb.writeln("import 'noto_font.dart';");
113+
sb.writeln();
114+
sb.writeln('final List<NotoFont> fallbackFonts = <NotoFont>[');
115+
for (final String family in fallbackFonts) {
116+
sb.write(" NotoFont.fromFlatRanges('$family', '${urlForFamily[family]!}', "
117+
'<int>[');
118+
for (final String range in charsetForFamily[family]!.split(' ')) {
119+
String? start;
120+
String? end;
121+
final List<String> parts = range.split('-');
122+
if (parts.length == 1) {
123+
start = parts[0];
124+
end = parts[0];
125+
} else {
126+
start = parts[0];
127+
end = parts[1];
128+
}
129+
sb.write('0x$start,0x$end,');
130+
}
131+
sb.writeln(']),');
132+
}
133+
sb.writeln('];');
134+
135+
final io.File fontDataFile = io.File(path.join(
136+
environment.webUiRootDir.path,
137+
'lib',
138+
'src',
139+
'engine',
140+
'canvaskit',
141+
'font_fallback_data.dart',
142+
));
143+
await fontDataFile.writeAsString(sb.toString());
144+
}
145+
}
146+
147+
const List<String> fallbackFonts = <String>[
148+
'Noto Sans',
149+
'Noto Emoji',
150+
'Noto Sans Symbols',
151+
'Noto Sans Symbols 2',
152+
'Noto Sans Adlam',
153+
'Noto Sans Anatolian Hieroglyphs',
154+
'Noto Sans Arabic',
155+
'Noto Sans Armenian',
156+
'Noto Sans Avestan',
157+
'Noto Sans Balinese',
158+
'Noto Sans Bamum',
159+
'Noto Sans Bassa Vah',
160+
'Noto Sans Batak',
161+
'Noto Sans Bengali',
162+
'Noto Sans Bhaiksuki',
163+
'Noto Sans Brahmi',
164+
'Noto Sans Buginese',
165+
'Noto Sans Buhid',
166+
'Noto Sans Canadian Aboriginal',
167+
'Noto Sans Carian',
168+
'Noto Sans Caucasian Albanian',
169+
'Noto Sans Chakma',
170+
'Noto Sans Cham',
171+
'Noto Sans Cherokee',
172+
'Noto Sans Coptic',
173+
'Noto Sans Cuneiform',
174+
'Noto Sans Cypriot',
175+
'Noto Sans Deseret',
176+
'Noto Sans Devanagari',
177+
'Noto Sans Duployan',
178+
'Noto Sans Egyptian Hieroglyphs',
179+
'Noto Sans Elbasan',
180+
'Noto Sans Elymaic',
181+
'Noto Sans Georgian',
182+
'Noto Sans Glagolitic',
183+
'Noto Sans Gothic',
184+
'Noto Sans Grantha',
185+
'Noto Sans Gujarati',
186+
'Noto Sans Gunjala Gondi',
187+
'Noto Sans Gurmukhi',
188+
'Noto Sans HK',
189+
'Noto Sans Hanunoo',
190+
'Noto Sans Hatran',
191+
'Noto Sans Hebrew',
192+
'Noto Sans Imperial Aramaic',
193+
'Noto Sans Indic Siyaq Numbers',
194+
'Noto Sans Inscriptional Pahlavi',
195+
'Noto Sans Inscriptional Parthian',
196+
'Noto Sans JP',
197+
'Noto Sans Javanese',
198+
'Noto Sans KR',
199+
'Noto Sans Kaithi',
200+
'Noto Sans Kannada',
201+
'Noto Sans Kayah Li',
202+
'Noto Sans Kharoshthi',
203+
'Noto Sans Khmer',
204+
'Noto Sans Khojki',
205+
'Noto Sans Khudawadi',
206+
'Noto Sans Lao',
207+
'Noto Sans Lepcha',
208+
'Noto Sans Limbu',
209+
'Noto Sans Linear A',
210+
'Noto Sans Linear B',
211+
'Noto Sans Lisu',
212+
'Noto Sans Lycian',
213+
'Noto Sans Lydian',
214+
'Noto Sans Mahajani',
215+
'Noto Sans Malayalam',
216+
'Noto Sans Mandaic',
217+
'Noto Sans Manichaean',
218+
'Noto Sans Marchen',
219+
'Noto Sans Masaram Gondi',
220+
'Noto Sans Math',
221+
'Noto Sans Mayan Numerals',
222+
'Noto Sans Medefaidrin',
223+
'Noto Sans Meetei Mayek',
224+
'Noto Sans Meroitic',
225+
'Noto Sans Miao',
226+
'Noto Sans Modi',
227+
'Noto Sans Mongolian',
228+
'Noto Sans Mro',
229+
'Noto Sans Multani',
230+
'Noto Sans Myanmar',
231+
'Noto Sans N Ko',
232+
'Noto Sans Nabataean',
233+
'Noto Sans New Tai Lue',
234+
'Noto Sans Newa',
235+
'Noto Sans Nushu',
236+
'Noto Sans Ogham',
237+
'Noto Sans Ol Chiki',
238+
'Noto Sans Old Hungarian',
239+
'Noto Sans Old Italic',
240+
'Noto Sans Old North Arabian',
241+
'Noto Sans Old Permic',
242+
'Noto Sans Old Persian',
243+
'Noto Sans Old Sogdian',
244+
'Noto Sans Old South Arabian',
245+
'Noto Sans Old Turkic',
246+
'Noto Sans Oriya',
247+
'Noto Sans Osage',
248+
'Noto Sans Osmanya',
249+
'Noto Sans Pahawh Hmong',
250+
'Noto Sans Palmyrene',
251+
'Noto Sans Pau Cin Hau',
252+
'Noto Sans Phags Pa',
253+
'Noto Sans Phoenician',
254+
'Noto Sans Psalter Pahlavi',
255+
'Noto Sans Rejang',
256+
'Noto Sans Runic',
257+
'Noto Sans SC',
258+
'Noto Sans Saurashtra',
259+
'Noto Sans Sharada',
260+
'Noto Sans Shavian',
261+
'Noto Sans Siddham',
262+
'Noto Sans Sinhala',
263+
'Noto Sans Sogdian',
264+
'Noto Sans Sora Sompeng',
265+
'Noto Sans Soyombo',
266+
'Noto Sans Sundanese',
267+
'Noto Sans Syloti Nagri',
268+
'Noto Sans Syriac',
269+
'Noto Sans TC',
270+
'Noto Sans Tagalog',
271+
'Noto Sans Tagbanwa',
272+
'Noto Sans Tai Le',
273+
'Noto Sans Tai Tham',
274+
'Noto Sans Tai Viet',
275+
'Noto Sans Takri',
276+
'Noto Sans Tamil',
277+
'Noto Sans Tamil Supplement',
278+
'Noto Sans Telugu',
279+
'Noto Sans Thaana',
280+
'Noto Sans Thai',
281+
'Noto Sans Tifinagh',
282+
'Noto Sans Tirhuta',
283+
'Noto Sans Ugaritic',
284+
'Noto Sans Vai',
285+
'Noto Sans Wancho',
286+
'Noto Sans Warang Citi',
287+
'Noto Sans Yi',
288+
'Noto Sans Zanabazar Square',
289+
];

lib/web_ui/lib/initialization.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ set debugEmulateFlutterTesterEnvironment(bool value) {
112112
engine.window.webOnlyDebugPhysicalSizeOverride =
113113
logicalSize * window.devicePixelRatio;
114114
}
115+
engine.debugDisableFontFallbacks = value;
115116
}
116117

117118
bool _debugEmulateFlutterTesterEnvironment = false;

lib/web_ui/lib/src/engine.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export 'engine/canvaskit/canvaskit_canvas.dart';
2626
export 'engine/canvaskit/color_filter.dart';
2727
export 'engine/canvaskit/embedded_views.dart';
2828
export 'engine/canvaskit/embedded_views_diff.dart';
29+
export 'engine/canvaskit/font_fallback_data.dart';
2930
export 'engine/canvaskit/font_fallbacks.dart';
3031
export 'engine/canvaskit/fonts.dart';
3132
export 'engine/canvaskit/image.dart';
@@ -38,6 +39,7 @@ export 'engine/canvaskit/layer_scene_builder.dart';
3839
export 'engine/canvaskit/layer_tree.dart';
3940
export 'engine/canvaskit/mask_filter.dart';
4041
export 'engine/canvaskit/n_way_canvas.dart';
42+
export 'engine/canvaskit/noto_font.dart';
4143
export 'engine/canvaskit/painting.dart';
4244
export 'engine/canvaskit/path.dart';
4345
export 'engine/canvaskit/path_metrics.dart';

0 commit comments

Comments
 (0)