Skip to content

Commit

Permalink
utils to update icon fonts
Browse files Browse the repository at this point in the history
  • Loading branch information
mosberger committed Jan 10, 2024
1 parent 23485ae commit 22aa7ac
Show file tree
Hide file tree
Showing 10 changed files with 3,311 additions and 0 deletions.
8 changes: 8 additions & 0 deletions font_scripts/.fantasticonrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
formatOptions: {
svg: {
centerHorizontally: true,
centerVertically: true
}
}
};
29 changes: 29 additions & 0 deletions font_scripts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
build/
10 changes: 10 additions & 0 deletions font_scripts/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "db7ef5bf9f59442b0e200a90587e8fa5e0c6336a"
channel: "stable"

project_type: package
1 change: 1 addition & 0 deletions font_scripts/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TODO: Add your license here.
16 changes: 16 additions & 0 deletions font_scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Font Scripts
Scripts to update the SBB CDN icon fonts from https://icons.app.sbb.ch/

## Usage
Make sure, npm and fvm flutter are installed.

### First usage
```shell
npm i
fvm flutter pub get
```

### every update
```shell
dart lib/update_icon_fonts.dart
```
4 changes: 4 additions & 0 deletions font_scripts/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
151 changes: 151 additions & 0 deletions font_scripts/lib/update_icon_fonts.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import 'dart:convert';
import 'dart:io';

import 'package:console_bars/console_bars.dart';
import 'package:http/http.dart';
import 'package:version/version.dart';

final baseUri = Uri.https('icons.app.sbb.ch');
final basePath = ['icons'];

late FillingBar progress;

Future<void> main() async {
final uri = makeUrlUri('index.json');
final response = await get(uri);
final decoded = utf8.decode(response.bodyBytes);
final responseJson = jsonDecode(decoded);
final versionFile = File('icons/.version');
final newVersion = responseJson['version'];

print('Version: $newVersion');
if (await versionFile.exists()) {
final oldVersion = await versionFile.readAsString();
if (Version.parse(oldVersion) >= Version.parse(newVersion)) {
print('already imported. exiting…');
return;
}
}

final icons = responseJson['icons'] as List<dynamic>;
final total = icons
.cast<Map<String, dynamic>>()
.where((e) =>
(e['tags'] as List).cast<String>().any((t) => t.contains('Size=')))
.length;
progress = FillingBar(total: total + 4, width: 10, desc: 'Downloading icons');

await prepareIcons('small', icons);
await prepareIcons('medium', icons);
await prepareIcons('large', icons);

await Directory('icons/small').create(recursive: true);
await Directory('icons/medium').create(recursive: true);
await Directory('icons/large').create(recursive: true);

progress.desc = 'Preparing icons';
await Process.run('npm', ['install'], runInShell: true);
progress.increment();
await Process.run('npm', ['run', 'fix'], runInShell: true);
progress.increment();
await Process.run('npm', ['run', 'generate'], runInShell: true);
progress.increment();
progress.desc = 'Generating dart files';
await createFlutterFontMap();
progress.increment();

await File('icons/.version').writeAsString(newVersion);
}

Uri makeUrlUri(String path) {
final uri = Uri(pathSegments: basePath.followedBy([path]));
return baseUri.resolveUri(uri);
}

Future<void> prepareIcons(String type, List<dynamic> icons) async {
final filter = icons.where((e) => e['tags'].contains('Size=$type'));
final dir = await Directory('icons/$type-tmp').create(recursive: true);
final downloads = <Future<void>>[];
for (final icon in filter) {
final name = icon['name'];
final fileName = '$name.svg';
final svgUri = makeUrlUri(fileName);
final download = downloadSvg(
svgUri,
fileName.replaceAll('-', '_'),
dir.path,
);
downloads.add(download);
}

await Future.wait(downloads);
}

Future<void> downloadSvg(Uri uri, String fileName, String path) async {
final svg = await get(uri);
await File('$path/$fileName').writeAsBytes(svg.bodyBytes);
progress.increment();
}

Future<void> createFlutterFontMap() async {
final sb = StringBuffer();

final small = await getMap('small');
final medium = await getMap('medium');
final large = await getMap('large');
mapFont(String name, String value) =>
sb.writeln(' static const ${name} = $value;');

sb.writeln('library sbb_icons;');
sb.writeln();
sb.writeln('import \'package:flutter/material.dart\';');
sb.writeln();
sb.writeln('const sbbIconSizeSmall = 24.0;');
sb.writeln('const sbbIconSizeMedium = 36.0;');
sb.writeln('const sbbIconSizeLarge = 48.0;');
sb.writeln();
sb.writeln('const smallFontFamily = \'packages/design_system_flutter/SBBIconsSmall\';');
sb.writeln('const mediumFontFamily = \'packages/design_system_flutter/SBBIconsMedium\';');
sb.writeln('const largeFontFamily = \'packages/design_system_flutter/SBBIconsLarge\';');
sb.writeln();
sb.writeln('sealed class SBBIcons {');
small.entries.forEach((e) => mapFont(e.key, e.value));
medium.entries.forEach((e) => mapFont(e.key, e.value));
large.entries.forEach((e) => mapFont(e.key, e.value));
sb.writeln('}');

await File('icons/sbb_icons.dart').writeAsString(sb.toString());

mapFont2(String name) =>
sb.writeln(' {\'icon\': SBBIcons.$name, \'name\': \'$name\'},');

sb.clear();
sb.writeln('import \'package:design_system_flutter/design_system_flutter.dart\';');
sb.writeln();
sb.writeln('sealed class SBBIconsIndex {');
sb.writeln(' static const iconsSmall = [');
small.entries.forEach((e) => mapFont2(e.key));
sb.writeln(' ];');
sb.writeln(' static const iconsMedium = [');
medium.entries.forEach((e) => mapFont2(e.key));
sb.writeln(' ];');
sb.writeln(' static const iconsLarge = [');
large.entries.forEach((e) => mapFont2(e.key));
sb.writeln(' ];');
sb.writeln('}');

await File('icons/sbb_icons_index.dart').writeAsString(sb.toString());
}

Future<Map<String, String>> getMap(String type) async {
final name = 'SBBIcons$type';
final fontFamily = '${type}FontFamily';
final uri = Uri.parse('icons/$name.json');
final file = File.fromUri(uri);
final json = await file.readAsString();
final map = jsonDecode(json) as Map<String, dynamic>;
return map.map((key, value) {
final v = value.toRadixString(16);
return MapEntry(key, 'IconData(0x$v, fontFamily: $fontFamily)');
});
}
Loading

0 comments on commit 22aa7ac

Please sign in to comment.