• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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> &#x2014; material icon named "$description".
220  static const IconData $identifier = IconData(0x$codepoint, fontFamily: 'MaterialIcons'$rtl);
221''';
222}
223