diff --git a/lib/src/icon_data.dart b/lib/src/icon_data.dart index b3db429..2822b8d 100644 --- a/lib/src/icon_data.dart +++ b/lib/src/icon_data.dart @@ -83,3 +83,16 @@ class IconDataThin extends IconData { fontPackage: 'font_awesome_flutter', ); } + +/// [IconData] for font awesome custom kit icon from a code point. Only works if +/// custom kit is enabled +/// +/// Code points can be obtained from fontawesome.com +class IconDataCustom extends IconData { + const IconDataCustom(int codePoint) + : super( + codePoint, + fontFamily: 'FontAwesomeCustom', + fontPackage: 'font_awesome_flutter', + ); +} diff --git a/pubspec.yaml b/pubspec.yaml index 0cfe6eb..73077a3 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,11 +36,15 @@ flutter: fonts: - asset: lib/fonts/fa-solid-900.ttf weight: 900 -# - family: FontAwesomeLight -# fonts: -# - asset: lib/fonts/fa-light-300.ttf -# weight: 300 -# - family: FontAwesomeThin -# fonts: -# - asset: lib/fonts/fa-thin-100.ttf -# weight: 100 + - family: FontAwesomeLight + fonts: + - asset: lib/fonts/fa-light-300.ttf + weight: 300 + - family: FontAwesomeThin + fonts: + - asset: lib/fonts/fa-thin-100.ttf + weight: 100 + - family: FontAwesomeCustom + fonts: + - asset: lib/fonts/custom-icons.ttf + weight: 400 \ No newline at end of file diff --git a/util/lib/custom_icon_transfer_object.dart b/util/lib/custom_icon_transfer_object.dart new file mode 100644 index 0000000..423eada --- /dev/null +++ b/util/lib/custom_icon_transfer_object.dart @@ -0,0 +1,73 @@ +class CustomIconTransferObject { + final List changes; + final List ligatures; + final SearchObject search; + final List styles; + final String unicode; + final String label; + final Map svg; + final List free; + + CustomIconTransferObject( + {this.changes = const ['6.4.0'], + this.ligatures = const [], + this.search = const SearchObject(), + this.styles = const ['custom'], + required this.unicode, + required this.label, + required this.svg, + this.free = const []}); + + Map toJson() { + final Map svgEntry = {}; + svg.forEach((key, value) { + svgEntry[key] = value.toJson(); + }); + return { + 'changes': changes, + 'ligatures': ligatures, + 'search': search.toJson(), + 'styles': styles, + 'unicode': unicode, + 'label': label, + 'svg': svgEntry, + 'free': free, + }; + } +} + +class SearchObject { + final List terms; + const SearchObject({this.terms = const []}); + factory SearchObject.fromJson(Map json) => + SearchObject(terms: (json['terms'] as List?)?.map((e) => e as String).toList() ?? []); + Map toJson() => {'terms': terms}; +} + +class SvgTransferObject { + SvgTransferObject( + {required this.lastModified, required this.raw, required this.viewBox, required this.width, required this.height, required this.path}); + final int? lastModified; + final String raw; + final List viewBox; + final int width; + final int height; + final String path; + + factory SvgTransferObject.fromJson(Map json) => SvgTransferObject( + lastModified: json['last_modified'], + raw: json['raw'], + viewBox: json['viewBox'], + width: json['width'], + height: json['height'], + path: json['path']); + + Map toJson() => { + 'last_modified': lastModified, + 'raw': raw, + 'viewBox': viewBox, + 'width': width, + 'height': height, + 'path': path, + }; +} diff --git a/util/lib/main.dart b/util/lib/main.dart index 88b77aa..560e4cf 100644 --- a/util/lib/main.dart +++ b/util/lib/main.dart @@ -5,9 +5,11 @@ import 'dart:io'; import 'package:ansicolor/ansicolor.dart'; import 'package:args/args.dart'; +import 'package:pub_semver/pub_semver.dart' as pub; import 'package:recase/recase.dart'; import 'package:version/version.dart'; -import 'package:pub_semver/pub_semver.dart' as pub; + +import 'custom_icon_transfer_object.dart'; /// A map which adjusts icon ids starting with a number /// @@ -102,23 +104,28 @@ void main(List rawArgs) async { print(blue('No icons.json found, updating free icons')); const repositoryName = 'FortAwesome/Font-Awesome'; final defaultBranch = await getRepositoryDefaultBranch(repositoryName); - print(blue( - 'Choosing branch "$defaultBranch" of repository https://github.com/' + - repositoryName)); + print(blue('Choosing branch "$defaultBranch" of repository https://github.com/' + repositoryName)); + await download('https://raw.githubusercontent.com/FortAwesome/Font-Awesome/$defaultBranch/metadata/icons.json', File('lib/fonts/icons.json')); await download( - 'https://raw.githubusercontent.com/FortAwesome/Font-Awesome/$defaultBranch/metadata/icons.json', - File('lib/fonts/icons.json')); - await download( - 'https://raw.githubusercontent.com/FortAwesome/Font-Awesome/$defaultBranch/webfonts/fa-brands-400.ttf', - File('lib/fonts/fa-brands-400.ttf')); - await download( - 'https://raw.githubusercontent.com/FortAwesome/Font-Awesome/$defaultBranch/webfonts/fa-regular-400.ttf', + 'https://raw.githubusercontent.com/FortAwesome/Font-Awesome/$defaultBranch/webfonts/fa-brands-400.ttf', File('lib/fonts/fa-brands-400.ttf')); + await download('https://raw.githubusercontent.com/FortAwesome/Font-Awesome/$defaultBranch/webfonts/fa-regular-400.ttf', File('lib/fonts/fa-regular-400.ttf')); await download( - 'https://raw.githubusercontent.com/FortAwesome/Font-Awesome/$defaultBranch/webfonts/fa-solid-900.ttf', - File('lib/fonts/fa-solid-900.ttf')); + 'https://raw.githubusercontent.com/FortAwesome/Font-Awesome/$defaultBranch/webfonts/fa-solid-900.ttf', File('lib/fonts/fa-solid-900.ttf')); } else { print(blue('Custom icons.json found, generating files')); + final customKitIconFont = File('lib/fonts/custom-icons.ttf'); + final hasCustomKitIcon = customKitIconFont.existsSync(); + if (hasCustomKitIcon && args['install-custom-kit']) { + print(blue('Custom Kit installation command found.')); + final customKitIconJS = File('lib/fonts/custom-icons.js'); + final hasCustomKitIconJS = customKitIconJS.existsSync(); + if (!hasCustomKitIconJS) { + print(red('Cannot find JS file for custom kit icons. Skip installing custom kit icon for now.')); + } else { + await updateIconJsonFile(iconsJson, customKitIconJS); + } + } } // A list of all versions mentioned in the metadata @@ -127,13 +134,11 @@ void main(List rawArgs) async { final Set styles = {}; // duotone icons are no longer supported final List excludedStyles = ['duotone', ...args['exclude']]; - var hasDuotoneIcons = readAndPickMetadata( - iconsJson, metadata, styles, versions, excludedStyles); + var hasDuotoneIcons = readAndPickMetadata(iconsJson, metadata, styles, versions, excludedStyles); if (hasDuotoneIcons) { // Duotone are no longer supported - temporarily added notice to avoid // confusion - print(red( - 'Duotone icons are no longer supported. Automatically disabled them.')); + print(red('Duotone icons are no longer supported. Automatically disabled them.')); } hasDuotoneIcons = false; @@ -213,9 +218,7 @@ void adjustPubspecFontIncludes(Set styles) { pubspecFile.writeAsStringSync(pubspec.join('\n')); print(blue('\nFound and enabled the following icon styles:')); - enabledStyles.isEmpty - ? print(red("None")) - : print(blue(enabledStyles.join(', '))); + enabledStyles.isEmpty ? print(red("None")) : print(blue(enabledStyles.join(', '))); print(blue('\nRunning "flutter pub get"')); final result = Process.runSync('flutter', ['pub', 'get'], runInShell: true); @@ -327,8 +330,7 @@ to complete successfully. } /// Builds the class with icon definitions and returns the output -List generateIconDefinitionClass( - List metadata, Version version) { +List generateIconDefinitionClass(List metadata, Version version) { final List output = [ 'library font_awesome_flutter;', '', @@ -476,12 +478,9 @@ Future printVersionNotice(String repositoryName) async { try { final packageVersion = pub.Version.parse(getPackageVersion()); - print(blue( - 'Using font_awesome_flutter version ' + packageVersion.toString())); + print(blue('Using font_awesome_flutter version ' + packageVersion.toString())); - await download( - 'https://api.github.com/repos/' + repositoryName + '/releases', - tmpFile); + await download('https://api.github.com/repos/' + repositoryName + '/releases', tmpFile); String rawReleasesData = await tmpFile.readAsString(); List releasesData = json.decode(rawReleasesData); @@ -491,12 +490,7 @@ Future printVersionNotice(String repositoryName) async { var releaseName = release["name"] as String; releaseName = releaseName.isEmpty ? release["tag_name"] : releaseName; // remove possible prefixes - releaseName = releaseName - .toLowerCase() - .replaceAll('version', '') - .replaceAll('v.', '') - .replaceAll('v', '') - .trim(); + releaseName = releaseName.toLowerCase().replaceAll('version', '').replaceAll('v.', '').replaceAll('v', '').trim(); final version = pub.Version.parse(releaseName); if (version.isPreRelease) { preReleases.add(version); @@ -515,15 +509,13 @@ Future printVersionNotice(String repositoryName) async { repositoryName + ')')); } - if (primaryPreRelease > packageVersion && - primaryPreRelease > primaryRelease) { + if (primaryPreRelease > packageVersion && primaryPreRelease > primaryRelease) { print(yellow('A pre-release version (' + primaryPreRelease.toString() + ') of font_awesome_flutter is available. Should you encounter any problems, have a look if it fixes them.')); } } on FormatException catch (_) { - print(red( - 'Error while getting font awesome flutter\'s version information. Could not determine whether you are using the latest version.')); + print(red('Error while getting font awesome flutter\'s version information. Could not determine whether you are using the latest version.')); } finally { tmpFile.delete(); } @@ -539,8 +531,7 @@ Future printVersionNotice(String repositoryName) async { /// latest font awesome version. /// [excludedStyles], which can be set in the program arguments, are removed. /// Returns whether the dataset contains duotone icons. -bool readAndPickMetadata(File iconsJson, List metadata, - Set styles, List versions, List excludedStyles) { +bool readAndPickMetadata(File iconsJson, List metadata, Set styles, List versions, List excludedStyles) { var hasDuotoneIcons = false; dynamic rawMetadata; @@ -548,8 +539,7 @@ bool readAndPickMetadata(File iconsJson, List metadata, final content = iconsJson.readAsStringSync(); rawMetadata = json.decode(content); } catch (_) { - print( - 'Error: Invalid icons.json. Please make sure you copied the correct file.'); + print('Error: Invalid icons.json. Please make sure you copied the correct file.'); exit(1); } @@ -625,11 +615,7 @@ Future download(String url, File target) async { ArgParser setUpArgParser() { final argParser = ArgParser(); - argParser.addFlag('help', - abbr: 'h', - defaultsTo: false, - negatable: false, - help: 'display program options and usage information'); + argParser.addFlag('help', abbr: 'h', defaultsTo: false, negatable: false, help: 'display program options and usage information'); argParser.addMultiOption('exclude', abbr: 'e', @@ -638,14 +624,54 @@ ArgParser setUpArgParser() { help: 'icon styles which are excluded by the generator'); argParser.addFlag('dynamic', - abbr: 'd', - defaultsTo: false, - negatable: false, - help: 'builds a map, which allows to dynamically retrieve icons by name'); + abbr: 'd', defaultsTo: false, negatable: false, help: 'builds a map, which allows to dynamically retrieve icons by name'); + + argParser.addFlag('install-custom-kit', abbr: 'k', defaultsTo: false, help: 'Install Custom Kit for PRO user'); return argParser; } +/// Update icon.json file to include custom kit icons +Future updateIconJsonFile(File iconsJson, File customKitIconJS) async { + Map customIcons = {}; + final lines = await customKitIconJS.readAsLines(); + final iconIndex = lines.indexWhere((line) => line.contains('var icons = {')); + final iconEndsIndex = lines.indexWhere((line) => line.contains('};'), iconIndex); + final customIconJson = ['{', lines.sublist(iconIndex + 1, iconEndsIndex).join().trim(), '}'].join(); + final json = jsonDecode(customIconJson) as Map; + json.forEach((key, value) { + // Json Value: + // "icon-name": [width, height, searchTerm, unicode, path] + final width = value[0]; + final height = value[1]; + final unicode = value[3]; + final path = value[4]; + // Convert icon name to camelCase + String mapKey = + key.splitMapJoin('-', onNonMatch: (group) => group.isEmpty ? group : '${group[0].toUpperCase()}${group.substring(1)}', onMatch: (_) => ''); + mapKey = mapKey[0].toLowerCase() + mapKey.substring(1); + customIcons[mapKey] = CustomIconTransferObject( + label: key.split('-').map((word) => word.isEmpty ? word : '${word[0].toUpperCase()}${word.substring(1)}').join(' '), + unicode: unicode, + svg: { + 'customKit': SvgTransferObject( + lastModified: DateTime.now().millisecondsSinceEpoch, + width: width, + height: height, + path: path, + viewBox: [0, 0, width, height], + raw: '', + ) + }); + }); + Map fileContent = jsonDecode(iconsJson.readAsStringSync()); + customIcons.forEach((key, value) { + fileContent[key] = value.toJson(); + }); + await iconsJson.writeAsString(jsonEncode(fileContent)); + print(yellow("icons.json file successfully updated")); +} + /// Displays the program help page. Accessible via the --help command line arg void displayHelp(ArgParser argParser) { var fileType = Platform.isWindows ? 'bat' : 'sh';