1// Copyright 2016 The Chromium 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// Regenerates the material icons file. 6// See https://github.com/flutter/flutter/wiki/Updating-Material-Design-Fonts 7 8import 'dart:convert' show LineSplitter; 9import 'dart:io'; 10 11import 'package:args/args.dart'; 12import 'package:path/path.dart' as path; 13 14const String kOptionCodepointsPath = 'codepoints'; 15const String kOptionIconsPath = 'icons'; 16const String kOptionDryRun = 'dry-run'; 17 18const String kDefaultCodepointsPath = 'bin/cache/artifacts/material_fonts/codepoints'; 19const String kDefaultIconsPath = 'packages/flutter/lib/src/material/icons.dart'; 20 21const String kBeginGeneratedMark = '// BEGIN GENERATED'; 22const String kEndGeneratedMark = '// END GENERATED'; 23 24const Map<String, String> kIdentifierRewrites = <String, String>{ 25 '360': 'threesixty', 26 '3d_rotation': 'threed_rotation', 27 '1k': 'one_k', 28 '2k': 'two_k', 29 '3k': 'three_k', 30 '4k': 'four_k', 31 '5k': 'five_k', 32 '6k': 'six_k', 33 '7k': 'seven_k', 34 '8k': 'eight_k', 35 '9k': 'nine_k', 36 '10k': 'ten_k', 37 '1k_plus': 'one_k_plus', 38 '2k_plus': 'two_k_plus', 39 '3k_plus': 'three_k_plus', 40 '4k_plus': 'four_k_plus', 41 '5k_plus': 'five_k_plus', 42 '6k_plus': 'six_k_plus', 43 '7k_plus': 'seven_k_plus', 44 '8k_plus': 'eight_k_plus', 45 '9k_plus': 'nine_k_plus', 46 '1mp': 'one_mp', 47 '2mp': 'two_mp', 48 '3mp': 'three_mp', 49 '4mp': 'four_mp', 50 '5mp': 'five_mp', 51 '6mp': 'six_mp', 52 '7mp': 'seven_mp', 53 '8mp': 'eight_mp', 54 '9mp': 'nine_mp', 55 '10mp': 'ten_mp', 56 '11mp': 'eleven_mp', 57 '12mp': 'twelve_mp', 58 '13mp': 'thirteen_mp', 59 '14mp': 'fourteen_mp', 60 '15mp': 'fifteen_mp', 61 '16mp': 'sixteen_mp', 62 '17mp': 'seventeen_mp', 63 '18mp': 'eighteen_mp', 64 '19mp': 'nineteen_mp', 65 '20mp': 'twenty_mp', 66 '21mp': 'twenty_one_mp', 67 '22mp': 'twenty_two_mp', 68 '23mp': 'twenty_three_mp', 69 '24mp': 'twenty_four_mp', 70 'class': 'class_', 71 72}; 73 74const Set<String> kMirroredIcons = <String>{ 75 // This list is obtained from: 76 // http://google.github.io/material-design-icons/#icons-in-rtl 77 'arrow_back', 78 'arrow_back_ios', 79 'arrow_forward', 80 'arrow_forward_ios', 81 'arrow_left', 82 'arrow_right', 83 'assignment', 84 'assignment_return', 85 'backspace', 86 'battery_unknown', 87 'call_made', 88 'call_merge', 89 'call_missed', 90 'call_missed_outgoing', 91 'call_received', 92 'call_split', 93 'chevron_left', 94 'chevron_right', 95 'chrome_reader_mode', 96 'device_unknown', 97 'dvr', 98 'event_note', 99 'featured_play_list', 100 'featured_video', 101 'first_page', 102 'flight_land', 103 'flight_takeoff', 104 'format_indent_decrease', 105 'format_indent_increase', 106 'format_list_bulleted', 107 'forward', 108 'functions', 109 'help', 110 'help_outline', 111 'input', 112 'keyboard_backspace', 113 'keyboard_tab', 114 'label', 115 'label_important', 116 'label_outline', 117 'last_page', 118 'launch', 119 'list', 120 'live_help', 121 'mobile_screen_share', 122 'multiline_chart', 123 'navigate_before', 124 'navigate_next', 125 'next_week', 126 'note', 127 'open_in_new', 128 'playlist_add', 129 'queue_music', 130 'redo', 131 'reply', 132 'reply_all', 133 'screen_share', 134 'send', 135 'short_text', 136 'show_chart', 137 'sort', 138 'star_half', 139 'subject', 140 'trending_flat', 141 'toc', 142 'trending_down', 143 'trending_up', 144 'undo', 145 'view_list', 146 'view_quilt', 147 'wrap_text', 148}; 149 150void main(List<String> args) { 151 // If we're run from the `tools` dir, set the cwd to the repo root. 152 if (path.basename(Directory.current.path) == 'tools') 153 Directory.current = Directory.current.parent.parent; 154 155 final ArgParser argParser = ArgParser(); 156 argParser.addOption(kOptionCodepointsPath, defaultsTo: kDefaultCodepointsPath); 157 argParser.addOption(kOptionIconsPath, defaultsTo: kDefaultIconsPath); 158 argParser.addFlag(kOptionDryRun, defaultsTo: false); 159 final ArgResults argResults = argParser.parse(args); 160 161 final File iconFile = File(path.absolute(argResults[kOptionIconsPath])); 162 if (!iconFile.existsSync()) { 163 stderr.writeln('Icons file not found: ${iconFile.path}'); 164 exit(1); 165 } 166 final File codepointsFile = File(path.absolute(argResults[kOptionCodepointsPath])); 167 if (!codepointsFile.existsSync()) { 168 stderr.writeln('Codepoints file not found: ${codepointsFile.path}'); 169 exit(1); 170 } 171 172 final String iconData = iconFile.readAsStringSync(); 173 final String codepointData = codepointsFile.readAsStringSync(); 174 final String newIconData = regenerateIconsFile(iconData, codepointData); 175 176 if (argResults[kOptionDryRun]) 177 stdout.writeln(newIconData); 178 else 179 iconFile.writeAsStringSync(newIconData); 180} 181 182String regenerateIconsFile(String iconData, String codepointData) { 183 final StringBuffer buf = StringBuffer(); 184 bool generating = false; 185 for (String line in LineSplitter.split(iconData)) { 186 if (!generating) 187 buf.writeln(line); 188 if (line.contains(kBeginGeneratedMark)) { 189 generating = true; 190 final String iconDeclarations = generateIconDeclarations(codepointData); 191 buf.write(iconDeclarations); 192 } else if (line.contains(kEndGeneratedMark)) { 193 generating = false; 194 buf.writeln(line); 195 } 196 } 197 return buf.toString(); 198} 199 200String generateIconDeclarations(String codepointData) { 201 return LineSplitter.split(codepointData) 202 .map<String>((String l) => l.trim()) 203 .where((String l) => l.isNotEmpty) 204 .map<String>(getIconDeclaration) 205 .join(); 206} 207 208String getIconDeclaration(String line) { 209 final List<String> tokens = line.split(' '); 210 if (tokens.length != 2) 211 throw FormatException('Unexpected codepoint data: $line'); 212 final String name = tokens[0]; 213 final String codepoint = tokens[1]; 214 final String identifier = kIdentifierRewrites[name] ?? name; 215 final String description = name.replaceAll('_', ' '); 216 final String rtl = kMirroredIcons.contains(name) ? ', matchTextDirection: true' : ''; 217 return ''' 218 219 /// <i class="material-icons md-36">$name</i> — material icon named "$description". 220 static const IconData $identifier = IconData(0x$codepoint, fontFamily: 'MaterialIcons'$rtl); 221'''; 222} 223