Unverified Commit 7782845e authored by Will Larche's avatar Will Larche Committed by GitHub

[Material] Updated icons and fonts (#61778)

parent 5142a304
flutter_infra/flutter/fonts/13ac995daa9dda0a6ba0a45f1fccc541e616a74c/fonts.zip flutter_infra/flutter/fonts/75d35d37aa818c40ffb521a8165dbe7d3c1cb8c4/fonts.zip
...@@ -12,7 +12,7 @@ import 'package:flutter_devicelab/framework/utils.dart'; ...@@ -12,7 +12,7 @@ import 'package:flutter_devicelab/framework/utils.dart';
final List<String> flutterAssets = <String>[ final List<String> flutterAssets = <String>[
'assets/flutter_assets/AssetManifest.json', 'assets/flutter_assets/AssetManifest.json',
'assets/flutter_assets/NOTICES', 'assets/flutter_assets/NOTICES',
'assets/flutter_assets/fonts/MaterialIcons-Regular.ttf', 'assets/flutter_assets/fonts/MaterialIcons-Regular.otf',
'assets/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf', 'assets/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf',
]; ];
......
...@@ -11,19 +11,23 @@ import 'dart:io'; ...@@ -11,19 +11,23 @@ import 'dart:io';
import 'package:args/args.dart'; import 'package:args/args.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
const String kOptionCodepointsPath = 'codepoints'; const String _newCodepointsPathOption = 'new-codepoints';
const String kOptionIconsPath = 'icons'; const String _oldCodepointsPathOption = 'old-codepoints';
const String kOptionDryRun = 'dry-run'; const String _iconsClassPathOption = 'icons';
const String _dryRunOption = 'dry-run';
const String kDefaultCodepointsPath = 'bin/cache/artifacts/material_fonts/codepoints'; const String _defaultNewCodepointsPath = 'codepoints';
const String kDefaultIconsPath = 'packages/flutter/lib/src/material/icons.dart'; const String _defaultOldCodepointsPath = 'bin/cache/artifacts/material_fonts/codepoints';
const String _defaultIconsPath = 'packages/flutter/lib/src/material/icons.dart';
const String kBeginGeneratedMark = '// BEGIN GENERATED'; const String _beginGeneratedMark = '// BEGIN GENERATED';
const String kEndGeneratedMark = '// END GENERATED'; const String _endGeneratedMark = '// END GENERATED';
const Map<String, String> kIdentifierRewrites = <String, String>{ const Map<String, String> _identifierRewrites = <String, String>{
'360': 'threesixty', '360': 'threesixty',
'3d_rotation': 'threed_rotation', '3d_rotation': 'threed_rotation',
'6_ft': 'six_ft',
'5g': 'five_g',
'1k': 'one_k', '1k': 'one_k',
'2k': 'two_k', '2k': 'two_k',
'3k': 'three_k', '3k': 'three_k',
...@@ -68,10 +72,9 @@ const Map<String, String> kIdentifierRewrites = <String, String>{ ...@@ -68,10 +72,9 @@ const Map<String, String> kIdentifierRewrites = <String, String>{
'23mp': 'twenty_three_mp', '23mp': 'twenty_three_mp',
'24mp': 'twenty_four_mp', '24mp': 'twenty_four_mp',
'class': 'class_', 'class': 'class_',
}; };
const Set<String> kMirroredIcons = <String>{ const Set<String> _mirroredIcons = <String>{
// This list is obtained from: // This list is obtained from:
// http://google.github.io/material-design-icons/#icons-in-rtl // http://google.github.io/material-design-icons/#icons-in-rtl
'arrow_back', 'arrow_back',
...@@ -152,44 +155,90 @@ void main(List<String> args) { ...@@ -152,44 +155,90 @@ void main(List<String> args) {
if (path.basename(Directory.current.path) == 'tools') if (path.basename(Directory.current.path) == 'tools')
Directory.current = Directory.current.parent.parent; Directory.current = Directory.current.parent.parent;
final ArgParser argParser = ArgParser(); final ArgResults argResults = _handleArguments(args);
argParser.addOption(kOptionCodepointsPath, defaultsTo: kDefaultCodepointsPath);
argParser.addOption(kOptionIconsPath, defaultsTo: kDefaultIconsPath);
argParser.addFlag(kOptionDryRun, defaultsTo: false);
final ArgResults argResults = argParser.parse(args);
final File iconFile = File(path.absolute(argResults[kOptionIconsPath] as String)); final File iconClassFile = File(path.normalize(path.absolute(argResults[_iconsClassPathOption] as String)));
if (!iconFile.existsSync()) { if (!iconClassFile.existsSync()) {
stderr.writeln('Icons file not found: ${iconFile.path}'); stderr.writeln('Error: Icons file not found: ${iconClassFile.path}');
exit(1);
}
final File newCodepointsFile = File(path.absolute(path.normalize(argResults[_newCodepointsPathOption] as String)));
if (!newCodepointsFile.existsSync()) {
stderr.writeln('Error: New codepoints file not found: ${newCodepointsFile.path}');
exit(1); exit(1);
} }
final File codepointsFile = File(path.absolute(argResults[kOptionCodepointsPath] as String)); final File oldCodepointsFile = File(path.absolute(argResults[_oldCodepointsPathOption] as String));
if (!codepointsFile.existsSync()) { if (!oldCodepointsFile.existsSync()) {
stderr.writeln('Codepoints file not found: ${codepointsFile.path}'); stderr.writeln('Error: Old codepoints file not found: ${oldCodepointsFile.path}');
exit(1); exit(1);
} }
final String iconData = iconFile.readAsStringSync(); final String newCodepointsString = newCodepointsFile.readAsStringSync();
final String codepointData = codepointsFile.readAsStringSync(); final Map<String, String> newTokenPairMap = _stringToTokenPairMap(newCodepointsString);
final String newIconData = regenerateIconsFile(iconData, codepointData);
final String oldCodepointsString = oldCodepointsFile.readAsStringSync();
final Map<String, String> oldTokenPairMap = _stringToTokenPairMap(oldCodepointsString);
if (argResults[kOptionDryRun] as bool) _testIsMapSuperset(newTokenPairMap, oldTokenPairMap);
final String iconClassFileData = iconClassFile.readAsStringSync();
stderr.writeln('Generating new token pairs.');
final String newIconData = _regenerateIconsFile(iconClassFileData, newTokenPairMap);
if (argResults[_dryRunOption] as bool) {
stdout.writeln(newIconData); stdout.writeln(newIconData);
else } else {
iconFile.writeAsStringSync(newIconData); stderr.writeln('\nWriting to ${iconClassFile.path}.');
iconClassFile.writeAsStringSync(newIconData);
_cleanUpFiles(newCodepointsFile, oldCodepointsFile);
}
} }
String regenerateIconsFile(String iconData, String codepointData) { ArgResults _handleArguments(List<String> args) {
final ArgParser argParser = ArgParser()
..addOption(_newCodepointsPathOption, defaultsTo: _defaultNewCodepointsPath)
..addOption(_oldCodepointsPathOption, defaultsTo: _defaultOldCodepointsPath)
..addOption(_iconsClassPathOption, defaultsTo: _defaultIconsPath)
..addFlag(_dryRunOption, defaultsTo: false);
return argParser.parse(args);
}
Map<String, String> _stringToTokenPairMap(String codepointData) {
final Iterable<String> cleanData = LineSplitter.split(codepointData)
.map((String line) => line.trim())
.where((String line) => line.isNotEmpty);
final Map<String, String> pairs = <String, String>{};
for (final String line in cleanData) {
final List<String> tokens = line.split(' ');
if (tokens.length != 2) {
throw FormatException('Unexpected codepoint data: $line');
}
pairs.putIfAbsent(tokens[0], () => tokens[1]);
}
return pairs;
}
String _regenerateIconsFile(String iconData, Map<String, String> tokenPairMap) {
final StringBuffer buf = StringBuffer(); final StringBuffer buf = StringBuffer();
bool generating = false; bool generating = false;
for (final String line in LineSplitter.split(iconData)) { for (final String line in LineSplitter.split(iconData)) {
if (!generating) if (!generating) {
buf.writeln(line); buf.writeln(line);
if (line.contains(kBeginGeneratedMark)) { }
if (line.contains(_beginGeneratedMark)) {
generating = true; generating = true;
final String iconDeclarations = generateIconDeclarations(codepointData);
buf.write(iconDeclarations); final String iconDeclarationsString = <String>[
} else if (line.contains(kEndGeneratedMark)) { for (MapEntry<String, String> entry in tokenPairMap.entries)
_generateDeclaration(entry)
].join();
buf.write(iconDeclarationsString);
} else if (line.contains(_endGeneratedMark)) {
generating = false; generating = false;
buf.writeln(line); buf.writeln(line);
} }
...@@ -197,26 +246,44 @@ String regenerateIconsFile(String iconData, String codepointData) { ...@@ -197,26 +246,44 @@ String regenerateIconsFile(String iconData, String codepointData) {
return buf.toString(); return buf.toString();
} }
String generateIconDeclarations(String codepointData) { void _testIsMapSuperset(Map<String, String> newCodepoints, Map<String, String> oldCodepoints) {
return LineSplitter.split(codepointData) final Set<String> newCodepointsSet = newCodepoints.keys.toSet();
.map<String>((String l) => l.trim()) final Set<String> oldCodepointsSet = oldCodepoints.keys.toSet();
.where((String l) => l.isNotEmpty)
.map<String>(getIconDeclaration) if (!newCodepointsSet.containsAll(oldCodepointsSet)) {
.join(); stderr.writeln(
'''Error: New codepoints file does not contain all the existing codepoints.\n
Missing: ${oldCodepointsSet.difference(newCodepointsSet)}
''',
);
exit(1);
}
} }
String getIconDeclaration(String line) { String _generateDeclaration(MapEntry<String, String> tokenPair) {
final List<String> tokens = line.split(' '); final String identifier = _generateIdentifier(tokenPair.key);
if (tokens.length != 2) final String description = tokenPair.key.replaceAll('_', ' ');
throw FormatException('Unexpected codepoint data: $line'); final String rtl = _mirroredIcons.contains(tokenPair.key)
final String name = tokens[0]; ? ', matchTextDirection: true'
final String codepoint = tokens[1]; : '';
final String identifier = kIdentifierRewrites[name] ?? name;
final String description = name.replaceAll('_', ' ');
final String rtl = kMirroredIcons.contains(name) ? ', matchTextDirection: true' : '';
return ''' return '''
/// <i class="material-icons md-36">$name</i> &#x2014; material icon named "$description". /// <i class="material-icons md-36">${tokenPair.key}</i> &#x2014; material icon named "$description".
static const IconData $identifier = IconData(0x$codepoint, fontFamily: 'MaterialIcons'$rtl); static const IconData $identifier = IconData(0x${tokenPair.value}, fontFamily: 'MaterialIcons'$rtl);
'''; ''';
} }
String _generateIdentifier(String rawIdentifier) {
for (final MapEntry<String, String> rewritePair in _identifierRewrites.entries) {
if (rawIdentifier.startsWith(rewritePair.key)) {
return rawIdentifier.replaceFirst(rewritePair.key, _identifierRewrites[rewritePair.key]);
}
}
return rawIdentifier;
}
// Replace the old codepoints file with the new.
void _cleanUpFiles(File newCodepointsFile, File oldCodepointsFile) {
stderr.writeln('\nMoving new codepoints file to ${oldCodepointsFile.path}.\n');
newCodepointsFile.renameSync(oldCodepointsFile.path);
}
...@@ -34,7 +34,7 @@ class FontLoader { ...@@ -34,7 +34,7 @@ class FontLoader {
/// Registers a font asset to be loaded by this font loader. /// Registers a font asset to be loaded by this font loader.
/// ///
/// The [bytes] argument specifies the actual font asset bytes. Currently, /// The [bytes] argument specifies the actual font asset bytes. Currently,
/// only TrueType (TTF) fonts are supported. /// only OpenType (OTF) and TrueType (TTF) fonts are supported.
void addFont(Future<ByteData> bytes) { void addFont(Future<ByteData> bytes) {
if (_loaded) if (_loaded)
throw StateError('FontLoader is already loaded'); throw StateError('FontLoader is already loaded');
......
...@@ -69,6 +69,10 @@ class IconTreeShaker { ...@@ -69,6 +69,10 @@ class IconTreeShaker {
/// The MIME type for ttf fonts. /// The MIME type for ttf fonts.
static const Set<String> kTtfMimeTypes = <String>{ static const Set<String> kTtfMimeTypes = <String>{
'font/ttf', // based on internet search 'font/ttf', // based on internet search
'font/opentype',
'font/otf',
'application/x-font-opentype',
'application/x-font-otf',
'application/x-font-ttf', // based on running locally. 'application/x-font-ttf', // based on running locally.
}; };
......
material: material:
- family: MaterialIcons - family: MaterialIcons
fonts: fonts:
- asset: fonts/MaterialIcons-Regular.ttf - asset: fonts/MaterialIcons-Regular.otf
...@@ -30,9 +30,9 @@ const String dartPath = '/flutter/dart'; ...@@ -30,9 +30,9 @@ const String dartPath = '/flutter/dart';
const String constFinderPath = '/flutter/const_finder.snapshot.dart'; const String constFinderPath = '/flutter/const_finder.snapshot.dart';
const String fontSubsetPath = '/flutter/font-subset'; const String fontSubsetPath = '/flutter/font-subset';
const String inputPath = '/input/fonts/MaterialIcons-Regular.ttf'; const String inputPath = '/input/fonts/MaterialIcons-Regular.otf';
const String outputPath = '/output/fonts/MaterialIcons-Regular.ttf'; const String outputPath = '/output/fonts/MaterialIcons-Regular.otf';
const String relativePath = 'fonts/MaterialIcons-Regular.ttf'; const String relativePath = 'fonts/MaterialIcons-Regular.otf';
List<String> getConstFinderArgs(String appDillPath) => <String>[ List<String> getConstFinderArgs(String appDillPath) => <String>[
dartPath, dartPath,
...@@ -270,7 +270,7 @@ void main() { ...@@ -270,7 +270,7 @@ void main() {
verify(mockProcessManager.start(fontSubsetArgs)).called(2); verify(mockProcessManager.start(fontSubsetArgs)).called(2);
}); });
testWithoutContext('Does not subset a non-ttf font', () async { testWithoutContext('Does not subset a non-supported font', () async {
final Environment environment = _createEnvironment(<String, String>{ final Environment environment = _createEnvironment(<String, String>{
kIconTreeShakerFlag: 'true', kIconTreeShakerFlag: 'true',
kBuildMode: 'release', kBuildMode: 'release',
...@@ -553,7 +553,7 @@ const String validFontManifestJson = ''' ...@@ -553,7 +553,7 @@ const String validFontManifestJson = '''
"family": "MaterialIcons", "family": "MaterialIcons",
"fonts": [ "fonts": [
{ {
"asset": "fonts/MaterialIcons-Regular.ttf" "asset": "fonts/MaterialIcons-Regular.otf"
} }
] ]
}, },
...@@ -581,7 +581,7 @@ const String invalidFontManifestJson = ''' ...@@ -581,7 +581,7 @@ const String invalidFontManifestJson = '''
"famly": "MaterialIcons", "famly": "MaterialIcons",
"fonts": [ "fonts": [
{ {
"asset": "fonts/MaterialIcons-Regular.ttf" "asset": "fonts/MaterialIcons-Regular.otf"
} }
] ]
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment