diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c5deea..f2ecc17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.3.0-nullsafety.0 + +* Migrate to null safety +* Fix unknown config parameters causing a crash and add related warnings + ## 0.2.0 * **IMPORTANT:** 'CFF' table is generated now instead of 'CFF2'. diff --git a/bin/fontify.dart b/bin/fontify.dart index 92bae45..d67f8e2 100644 --- a/bin/fontify.dart +++ b/bin/fontify.dart @@ -14,7 +14,7 @@ final _argParser = ArgParser(allowTrailingOptions: true); void main(List args) { defineOptions(_argParser); - CliArguments parsedArgs; + late final CliArguments parsedArgs; try { parsedArgs = parseArgsAndConfig(_argParser, args); @@ -29,7 +29,7 @@ void main(List args) { try { _run(parsedArgs); - } on dynamic catch (e) { + } on Object catch (e) { logger.e(e.toString()); exit(65); } @@ -47,11 +47,11 @@ void _run(CliArguments parsedArgs) { if (parsedArgs.classFile?.existsSync() ?? false) { logger.v( - 'Output file for a Flutter class already exists (${parsedArgs.classFile.path}) - ' + 'Output file for a Flutter class already exists (${parsedArgs.classFile!.path}) - ' 'overwriting it'); } - if (parsedArgs.fontFile?.existsSync() ?? false) { + if (!parsedArgs.fontFile.existsSync()) { logger.v( 'Output file for a font file already exists (${parsedArgs.fontFile.path}) - ' 'overwriting it'); @@ -96,7 +96,7 @@ void _run(CliArguments parsedArgs) { package: parsedArgs.fontPackage, ); - parsedArgs.classFile.writeAsStringSync(classString); + parsedArgs.classFile!.writeAsStringSync(classString); } logger.i('Generated in ${stopwatch.elapsedMilliseconds}ms'); @@ -112,7 +112,7 @@ void _usageError(String error) { exit(64); } -void _printUsage([String error]) { +void _printUsage([String? error]) { final message = error ?? _kAbout; stdout.write(''' diff --git a/lib/src/cli/arguments.dart b/lib/src/cli/arguments.dart index 1977306..75af690 100644 --- a/lib/src/cli/arguments.dart +++ b/lib/src/cli/arguments.dart @@ -121,7 +121,7 @@ class CliArguments { /// /// Throws [CliArgumentException], if there is an error in arg parsing /// or if argument has wrong type. - factory CliArguments.fromMap(Map rawArgMap) { + factory CliArguments.fromMap(Map rawArgMap) { // Validating types for (final e in _kArgAllowedTypes.entries) { final arg = e.key; @@ -136,58 +136,36 @@ class CliArguments { } final map = formatArguments(rawArgMap); + _validate(map); return CliArguments( map[CliArgument.svgDir] as Directory, map[CliArgument.fontFile] as File, - map[CliArgument.classFile] as File, - map[CliArgument.className] as String, - map[CliArgument.indent] as int, - map[CliArgument.fontPackage] as String, - map[CliArgument.fontName] as String, - map[CliArgument.recursive] as bool, - map[CliArgument.ignoreShapes] as bool, - map[CliArgument.normalize] as bool, - map[CliArgument.verbose] as bool, - map[CliArgument.configFile] as File, + map[CliArgument.classFile] as File?, + map[CliArgument.className] as String?, + map[CliArgument.indent] as int?, + map[CliArgument.fontPackage] as String?, + map[CliArgument.fontName] as String?, + map[CliArgument.recursive] as bool?, + map[CliArgument.ignoreShapes] as bool?, + map[CliArgument.normalize] as bool?, + map[CliArgument.verbose] as bool?, + map[CliArgument.configFile] as File?, ); } final Directory svgDir; final File fontFile; - final File classFile; - final String className; - final String fontPackage; - final int indent; - final String fontName; - final bool recursive; - final bool ignoreShapes; - final bool normalize; - final bool verbose; - final File configFile; - - /// Validates CLI arguments. - /// - /// Throws [CliArgumentException], if argument is not valid. - void validate() { - if (svgDir == null) { - throw CliArgumentException('The input directory is not specified.'); - } - - if (fontFile == null) { - throw CliArgumentException('The output font file is not specified.'); - } - - if (svgDir.statSync().type != FileSystemEntityType.directory) { - throw CliArgumentException( - "The input directory is not a directory or it doesn't exist."); - } - - if (indent != null && indent < 0) { - throw CliArgumentException( - 'indent must be a non-negative integer, was $indent.'); - } - } + final File? classFile; + final String? className; + final String? fontPackage; + final int? indent; + final String? fontName; + final bool? recursive; + final bool? ignoreShapes; + final bool? normalize; + final bool? verbose; + final File? configFile; } /// Parses argument list. @@ -196,7 +174,7 @@ class CliArguments { /// /// Returns an instance of [CliArguments] containing all parsed data. CliArguments parseArguments(ArgParser argParser, List args) { - ArgResults argResults; + late final ArgResults argResults; try { argResults = argParser.parse(args); } on FormatException catch (err) { @@ -210,7 +188,7 @@ CliArguments parseArguments(ArgParser argParser, List args) { final posArgsLength = math.min(_kPositionalArguments.length, argResults.rest.length); - final rawArgMap = { + final rawArgMap = { for (final e in kOptionNames.entries) e.key: argResults[e.value], for (var i = 0; i < posArgsLength; i++) _kPositionalArguments[i]: argResults.rest[i], @@ -219,30 +197,48 @@ CliArguments parseArguments(ArgParser argParser, List args) { return CliArguments.fromMap(rawArgMap); } +MapEntry? _mapConfigKeyEntry( + MapEntry e, +) { + final dynamic rawKey = e.key; + void logUnknown() => logger.w('Unknown config parameter "$rawKey"'); + + if (rawKey is! String) { + logUnknown(); + return null; + } + + final key = kConfigKeys.getKeyForValue(rawKey); + if (key == null) { + logUnknown(); + return null; + } + + return MapEntry(key, e.value); +} + /// Parses config file. /// /// Returns an instance of [CliArguments] containing all parsed data or null, /// if 'fontify' key is not present in config file. -CliArguments parseConfig(String config) { - final Object parsedYaml = loadYaml(config); +CliArguments? parseConfig(String config) { + final dynamic yamlMap = loadYaml(config); - if (parsedYaml is! YamlMap) { + if (yamlMap is! YamlMap) { return null; } - final yamlMap = parsedYaml as YamlMap; - final Object fontifyYaml = yamlMap['fontify']; + final dynamic fontifyYamlMap = yamlMap['fontify']; - if (fontifyYaml is! YamlMap) { + if (fontifyYamlMap is! YamlMap) { return null; } - final fontifyYamlMap = fontifyYaml as YamlMap; + final entries = fontifyYamlMap.entries + .map(_mapConfigKeyEntry) + .whereType>(); - final argMap = { - for (final e in fontifyYamlMap.entries) - if (e.key is String) kConfigKeys.getKeyForValue(e.key as String): e.value, - }; + final argMap = Map.fromEntries(entries); return CliArguments.fromMap(argMap); } @@ -259,7 +255,7 @@ CliArguments parseArgsAndConfig(ArgParser argParser, List args) { final configList = [parsedArgs.configFile, ...defaultConfigList]; for (final configFile in configList) { - if (configFile?.existsSync() ?? false) { + if (configFile != null && configFile.existsSync()) { final parsedConfig = parseConfig(configFile.readAsStringSync()); if (parsedConfig != null) { @@ -270,10 +266,36 @@ CliArguments parseArgsAndConfig(ArgParser argParser, List args) { } } - parsedArgs.validate(); return parsedArgs; } +/// Validates CLI arguments. +/// +/// Throws [CliArgumentException], if argument is not valid. +void _validate(Map args) { + final svgDir = args[CliArgument.svgDir] as Directory?; + final fontFile = args[CliArgument.fontFile] as File?; + final indent = args[CliArgument.indent] as int?; + + if (svgDir == null) { + throw CliArgumentException('The input directory is not specified.'); + } + + if (fontFile == null) { + throw CliArgumentException('The output font file is not specified.'); + } + + if (svgDir.statSync().type != FileSystemEntityType.directory) { + throw CliArgumentException( + "The input directory is not a directory or it doesn't exist."); + } + + if (indent != null && indent < 0) { + throw CliArgumentException( + 'indent must be a non-negative integer, was $indent.'); + } +} + class CliArgumentException implements Exception { CliArgumentException(this.message); @@ -284,3 +306,6 @@ class CliArgumentException implements Exception { } class CliHelpException implements Exception {} + +// Ignoring as CLI arguments are dynamically typed +// ignore_for_file: avoid_annotating_with_dynamic diff --git a/lib/src/cli/formatter.dart b/lib/src/cli/formatter.dart index fc75ec0..bba085e 100644 --- a/lib/src/cli/formatter.dart +++ b/lib/src/cli/formatter.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'arguments.dart'; -typedef CliArgumentFormatter = Object Function(Object arg); +typedef CliArgumentFormatter = dynamic Function(dynamic arg); const _kArgumentFormatters = { CliArgument.svgDir: _dir, @@ -12,11 +12,11 @@ const _kArgumentFormatters = { CliArgument.configFile: _file, }; -Directory _dir(Object arg) => arg == null ? null : Directory(arg as String); +Directory _dir(dynamic arg) => Directory(arg as String); -File _file(Object arg) => arg == null ? null : File(arg as String); +File _file(dynamic arg) => File(arg as String); -int _indent(Object arg) { +int _indent(dynamic arg) { if (arg is int) { return arg; } @@ -29,13 +29,11 @@ int _indent(Object arg) { throwException(); } - int indent; + late final int indent; final indentArg = arg as String; try { - if (indentArg != null) { - indent = int.parse(indentArg); - } + indent = int.parse(indentArg); } on FormatException catch (_) { throwException(); } @@ -44,14 +42,17 @@ int _indent(Object arg) { } /// Formats arguments. -Map formatArguments(Map args) { - return args.map((k, v) { +Map formatArguments(Map args) { + return args.map((k, dynamic v) { final formatter = _kArgumentFormatters[k]; - if (formatter == null) { - return MapEntry(k, v); + if (formatter == null || v == null) { + return MapEntry(k, v); } - return MapEntry(k, formatter(v)); + return MapEntry(k, formatter(v)); }); } + +// Ignoring as CLI arguments are dynamically typed +// ignore_for_file: avoid_annotating_with_dynamic diff --git a/lib/src/cli/options.dart b/lib/src/cli/options.dart index b76f612..8ef3427 100644 --- a/lib/src/cli/options.dart +++ b/lib/src/cli/options.dart @@ -6,27 +6,27 @@ void defineOptions(ArgParser argParser) { argParser ..addSeparator('Flutter class options:') ..addOption( - kOptionNames[CliArgument.classFile], + kOptionNames[CliArgument.classFile]!, abbr: 'o', help: 'Output path for Flutter-compatible class that contains identifiers for the icons.', valueHelp: 'path', ) ..addOption( - kOptionNames[CliArgument.indent], + kOptionNames[CliArgument.indent]!, abbr: 'i', help: 'Number of spaces in leading indentation for Flutter class file.', valueHelp: 'indent', defaultsTo: '2', ) ..addOption( - kOptionNames[CliArgument.className], + kOptionNames[CliArgument.className]!, abbr: 'c', help: 'Name for a generated class.', valueHelp: 'name', ) ..addOption( - kOptionNames[CliArgument.fontPackage], + kOptionNames[CliArgument.fontPackage]!, abbr: 'p', help: 'Name of a package that provides a font. Used to provide a font through package dependency.', @@ -34,46 +34,46 @@ void defineOptions(ArgParser argParser) { ) ..addSeparator('Font options:') ..addOption( - kOptionNames[CliArgument.fontName], + kOptionNames[CliArgument.fontName]!, abbr: 'f', help: 'Name for a generated font.', valueHelp: 'name', ) ..addFlag( - kOptionNames[CliArgument.normalize], + kOptionNames[CliArgument.normalize]!, help: 'Enables glyph normalization for the font. Disable this if every icon has the same size and positioning.', defaultsTo: true, ) ..addFlag( - kOptionNames[CliArgument.ignoreShapes], + kOptionNames[CliArgument.ignoreShapes]!, help: 'Disables SVG shape-to-path conversion (circle, rect, etc.).', defaultsTo: true, ) ..addSeparator('Other options:') ..addOption( - kOptionNames[CliArgument.configFile], + kOptionNames[CliArgument.configFile]!, abbr: 'z', help: 'Path to Fontify yaml configuration file. pubspec.yaml and fontify.yaml files are used by default.', valueHelp: 'path', ) ..addFlag( - kOptionNames[CliArgument.recursive], + kOptionNames[CliArgument.recursive]!, abbr: 'r', help: 'Recursively look for .svg files.', defaultsTo: kDefaultRecursive, negatable: false, ) ..addFlag( - kOptionNames[CliArgument.verbose], + kOptionNames[CliArgument.verbose]!, abbr: 'v', help: 'Display every logging message.', defaultsTo: kDefaultVerbose, negatable: false, ) ..addFlag( - kOptionNames[CliArgument.help], + kOptionNames[CliArgument.help]!, abbr: 'h', help: 'Shows this usage information.', negatable: false, diff --git a/lib/src/common/api.dart b/lib/src/common/api.dart index 7e6ff7e..4e976f9 100644 --- a/lib/src/common/api.dart +++ b/lib/src/common/api.dart @@ -1,5 +1,3 @@ -import 'package:meta/meta.dart'; - import '../otf.dart'; import '../svg.dart'; import '../utils/flutter_class_gen.dart'; @@ -30,12 +28,11 @@ class SvgToOtfResult { /// /// Returns an instance of [SvgToOtfResult] class containing glyphs and a font. SvgToOtfResult svgToOtf({ - @required Map svgMap, - bool ignoreShapes, - bool normalize, - String fontName, + required Map svgMap, + bool? ignoreShapes, + bool? normalize, + String? fontName, }) { - svgMap ??= {}; normalize ??= true; final svgList = [ @@ -80,12 +77,12 @@ SvgToOtfResult svgToOtf({ /// /// Returns content of a class file. String generateFlutterClass({ - @required List glyphList, - String className, - String familyName, - String fontFileName, - String package, - int indent, + required List glyphList, + String? className, + String? familyName, + String? fontFileName, + String? package, + int? indent, }) { final generator = FlutterClassGenerator( glyphList, diff --git a/lib/src/common/generic_glyph.dart b/lib/src/common/generic_glyph.dart index e0ec97c..eb12e7c 100644 --- a/lib/src/common/generic_glyph.dart +++ b/lib/src/common/generic_glyph.dart @@ -17,8 +17,8 @@ import 'outline.dart'; class GenericGlyphMetadata { GenericGlyphMetadata({this.charCode, this.name}); - int charCode; - String name; + int? charCode; + String? name; /// Deep copy GenericGlyphMetadata copy() { @@ -49,7 +49,7 @@ class GenericGlyphMetrics { /// Used as an intermediate storage between different types of glyphs /// (including OpenType's CharString, TrueType outlines). class GenericGlyph { - GenericGlyph(this.outlines, this.bounds, [GenericGlyphMetadata metadata]) + GenericGlyph(this.outlines, this.bounds, [GenericGlyphMetadata? metadata]) : metadata = metadata ?? GenericGlyphMetadata(); GenericGlyph.empty() @@ -212,21 +212,20 @@ class GenericGlyph { } /// Resizes according to ascender/descender or a font height. - GenericGlyph resize({int ascender, int descender, int fontHeight}) { - assert((ascender != null && descender != null) || fontHeight != null, - 'Wrong parameters for resizing'); - + GenericGlyph resize({int? ascender, int? descender, int? fontHeight}) { final metrics = this.metrics; - int longestSide; - double sideRatio; + late final int longestSide; + late final double sideRatio; - if (fontHeight == null) { + if (ascender != null && descender != null) { longestSide = math.max(metrics.height, metrics.width); sideRatio = (ascender + descender) / longestSide; - } else { + } else if (fontHeight != null) { longestSide = bounds.height.toInt(); sideRatio = fontHeight / longestSide; + } else { + throw ArgumentError('Wrong parameters for resizing'); } // No need to resize diff --git a/lib/src/otf/cff/char_string.dart b/lib/src/otf/cff/char_string.dart index f9f4683..5e55ceb 100644 --- a/lib/src/otf/cff/char_string.dart +++ b/lib/src/otf/cff/char_string.dart @@ -11,7 +11,7 @@ import 'operand.dart'; import 'operator.dart'; class CharStringOperand extends CFFOperand { - CharStringOperand(num value, [int size]) : super(value, size); + CharStringOperand(num? value, [int? size]) : super(value, size); factory CharStringOperand.fromByteData( ByteData byteData, int offset, int b0) { @@ -31,7 +31,7 @@ class CharStringOperand extends CFFOperand { if (value is double) { byteData ..setUint8(offset++, 255) - ..setUint32(offset, (value * 0x10000).round().toInt()); + ..setUint32(offset, (value! * 0x10000).round().toInt()); offset += 4; } else { super.encodeToBinary(byteData); @@ -211,7 +211,7 @@ class CharStringInterpreter { } } - void _pushCommand(Iterable operandValues, int opB0, [int opB1]) { + void _pushCommand(Iterable operandValues, int opB0, [int? opB1]) { final command = CharStringCommand( CFFOperator(CFFOperatorContext.charString, opB0, opB1), operandValues.map((e) => CharStringOperand(e)).toList()); @@ -232,7 +232,7 @@ class CharStringInterpreter { if (op == 28 || op >= 32) { final operandByteData = byteData.sublistView(offset); final operand = CharStringOperand.fromByteData(operandByteData, 0, op); - _stack.add(operand.value); + _stack.add(operand.value!); } else { switch (op) { case 1: // hstem @@ -249,7 +249,7 @@ class CharStringInterpreter { break; case 5: // rlineto - final arguments = []; + final arguments = []; while (_stack.length >= 2) { final dx = _stack.removeFirstOrZero(); @@ -263,7 +263,7 @@ class CharStringInterpreter { case 6: // hlineto case 7: // vlineto - final arguments = []; + final arguments = []; var isX = op == 6; while (_stack.isNotEmpty) { @@ -282,7 +282,7 @@ class CharStringInterpreter { break; case 8: // rrcurveto - final arguments = []; + final arguments = []; while (_stack.length >= 6) { final dxc1 = _stack.removeFirstOrZero(); @@ -332,7 +332,7 @@ class CharStringInterpreter { break; case 24: // rcurveline - final arguments = []; + final arguments = []; while (_stack.length >= 8) { final dxc1 = _stack.removeFirstOrZero(); @@ -352,7 +352,7 @@ class CharStringInterpreter { break; case 25: // rlinecurve - final arguments = []; + final arguments = []; while (_stack.length >= 8) { final dx = _stack.removeFirstOrZero(); @@ -372,7 +372,7 @@ class CharStringInterpreter { break; case 26: // vvcurveto - final arguments = []; + final arguments = []; if (_stack.length.isOdd) { final dx = _stack.removeFirstOrZero(); @@ -393,7 +393,7 @@ class CharStringInterpreter { break; case 27: // hhcurveto - final arguments = []; + final arguments = []; if (_stack.length.isOdd) { final dy = _stack.removeFirstOrZero(); @@ -416,7 +416,7 @@ class CharStringInterpreter { case 30: // vhcurveto case 31: // hvcurveto var isX = op == 31; - final arguments = []; + final arguments = []; while (_stack.length >= 4) { if (isX) { @@ -482,7 +482,7 @@ class CharStringInterpreter { ByteData writeCommands( List commandList, { - int glyphWidth, + int? glyphWidth, }) { final list = []; @@ -519,7 +519,7 @@ class CharStringInterpreterLimits { } extension _QueueExt on Queue { - T removeFirstOrZero() { + T? removeFirstOrZero() { if (isEmpty) { if (T == num) { return 0 as T; diff --git a/lib/src/otf/cff/charset.dart b/lib/src/otf/cff/charset.dart index da2b1a7..219f487 100644 --- a/lib/src/otf/cff/charset.dart +++ b/lib/src/otf/cff/charset.dart @@ -9,9 +9,9 @@ const _kRange1Size = 3; abstract class CharsetEntry implements BinaryCodable { const CharsetEntry(this.format); - factory CharsetEntry.fromByteData(ByteData byteData, int glyphCount) { - final format = byteData.getUint8(0); - byteData = byteData.sublistView(1); + static CharsetEntry? fromByteData(ByteData bd, int glyphCount) { + final format = bd.getUint8(0); + final byteData = bd.sublistView(1); switch (format) { case _kFormat1: diff --git a/lib/src/otf/cff/dict.dart b/lib/src/otf/cff/dict.dart index 03f1060..b9ce40e 100644 --- a/lib/src/otf/cff/dict.dart +++ b/lib/src/otf/cff/dict.dart @@ -1,4 +1,5 @@ import 'dart:typed_data'; +import 'package:collection/collection.dart'; import '../../common/codable/binary.dart'; import '../../utils/exception.dart'; @@ -21,7 +22,7 @@ class CFFDictEntry extends BinaryCodable { if (b0 < 28) { /// Reading an operator (b0 is not in operand range) - int b1; + int? b1; if (b0 == _kOperatorEscapeByte) { /// An operator is 2-byte long @@ -112,9 +113,8 @@ class CFFDict extends BinaryCodable { List entryList; - CFFDictEntry getEntryForOperator(CFFOperator operator) { - return entryList.firstWhere((e) => e.operator == operator, - orElse: () => null); + CFFDictEntry? getEntryForOperator(CFFOperator operator) { + return entryList.firstWhereOrNull((e) => e.operator == operator); } @override diff --git a/lib/src/otf/cff/index.dart b/lib/src/otf/cff/index.dart index 59faeb0..eef91c6 100644 --- a/lib/src/otf/cff/index.dart +++ b/lib/src/otf/cff/index.dart @@ -11,7 +11,7 @@ class CFFIndex extends BinaryCodable { CFFIndex.empty(this.isCFF1) : count = 0, - offSize = null, + offSize = 1, offsetList = []; factory CFFIndex.fromByteData(ByteData byteData, bool isCFF1) { @@ -25,10 +25,7 @@ class CFFIndex extends BinaryCodable { } final offSize = byteData.getUint8(offset++); - - if (offSize < 1 || offSize > 4) { - throw TableDataFormatException('Wrong offSize value'); - } + _validateOffSize(offSize); final offsetList = []; @@ -53,8 +50,16 @@ class CFFIndex extends BinaryCodable { bool get isEmpty => count == 0; + static void _validateOffSize(int offSize) { + if (offSize < 1 || offSize > 4) { + throw TableDataFormatException('Wrong offSize value'); + } + } + @override void encodeToBinary(ByteData byteData) { + _validateOffSize(offSize); + var offset = 0; if (isCFF1) { @@ -126,7 +131,7 @@ class CFFIndexWithData implements BinaryCodable, CalculatableOffsets { factory CFFIndexWithData.create(List data, bool isCFF1) => CFFIndexWithData(null, data, isCFF1); - CFFIndex index; + CFFIndex? index; final List data; final bool isCFF1; @@ -218,8 +223,18 @@ class CFFIndexWithData implements BinaryCodable, CalculatableOffsets { return newIndex.size + newIndex.offsetList.last - 1; } + CFFIndex get _guardedIndex { + if (index == null) { + throw StateError('index must not be null'); + } + + return index!; + } + @override void encodeToBinary(ByteData byteData) { + final index = _guardedIndex; + if (data.isEmpty) { index.encodeToBinary(byteData.sublistView(0, index.size)); } diff --git a/lib/src/otf/cff/operand.dart b/lib/src/otf/cff/operand.dart index c659be2..6fc405c 100644 --- a/lib/src/otf/cff/operand.dart +++ b/lib/src/otf/cff/operand.dart @@ -113,17 +113,17 @@ class CFFOperand extends BinaryCodable { } /// Either real or integer number - final num value; + final num? value; /// Weather should force max-length integer or not (used for offset pointers) final bool forceLargeInt; - int _size; + int? _size; /// Returns normalized string representation of a real number String _doubleToNormalizedString() { if (value is! double) { - return null; + throw StateError('Value type is not double'); } var string = value.toString(); @@ -140,15 +140,24 @@ class CFFOperand extends BinaryCodable { return string; } + num get _guardedValue { + if (value == null) { + throw StateError('Value must not be null'); + } + + return value!; + } + @override void encodeToBinary(ByteData byteData) { var offset = 0; + final value = _guardedValue; void writeDouble() { final s = _doubleToNormalizedString(); var firstHalf = true; - int prevNibble; + late int prevNibble; for (var i = 0; i < s.length; i++) { var char = s[i]; @@ -213,9 +222,11 @@ class CFFOperand extends BinaryCodable { @override int get size { if (_size != null) { - return _size; + return _size!; } + final value = _guardedValue; + return _size = () { if (value is double) { var valueString = _doubleToNormalizedString(); diff --git a/lib/src/otf/cff/operator.dart b/lib/src/otf/cff/operator.dart index a65471a..194e6bc 100644 --- a/lib/src/otf/cff/operator.dart +++ b/lib/src/otf/cff/operator.dart @@ -14,7 +14,7 @@ class CFFOperator implements BinaryCodable { : intValue = b1 != null ? ((b0 << 8) | b1) : b0; final int b0; - final int b1; + final int? b1; final int intValue; final CFFOperatorContext context; @@ -38,7 +38,7 @@ class CFFOperator implements BinaryCodable { byteData.setUint8(0, b0); if (b1 != null) { - byteData.setUint8(1, b1); + byteData.setUint8(1, b1!); } } @@ -48,10 +48,14 @@ class CFFOperator implements BinaryCodable { switch (context) { case CFFOperatorContext.dict: - name = dictOperatorNames[this]; + if (dictOperatorNames.containsKey(this)) { + name = dictOperatorNames[this]!; + } break; case CFFOperatorContext.charString: - name = charStringOperatorNames[this]; + if (charStringOperatorNames.containsKey(this)) { + name = charStringOperatorNames[this]!; + } break; } diff --git a/lib/src/otf/otf.dart b/lib/src/otf/otf.dart index 47e13d6..fd45270 100644 --- a/lib/src/otf/otf.dart +++ b/lib/src/otf/otf.dart @@ -1,10 +1,9 @@ import 'dart:typed_data'; -import 'package:meta/meta.dart'; - import '../common/calculatable_offsets.dart'; import '../common/codable/binary.dart'; import '../common/generic_glyph.dart'; +import '../utils.dart'; import '../utils/misc.dart'; import '../utils/otf.dart'; @@ -64,14 +63,14 @@ class OpenTypeFont implements BinaryCodable { /// glyphs are resized and centered to fit in coordinates grid (unitsPerEm). /// Defaults to true. factory OpenTypeFont.createFromGlyphs({ - @required List glyphList, - String fontName, - String description, - Revision revision, - String achVendID, - bool useOpenType, - bool usePostV2, - bool normalize, + required List glyphList, + String? fontName, + String? description, + Revision? revision, + String? achVendID, + bool? useOpenType, + bool? usePostV2, + bool? normalize, }) { if (fontName?.isEmpty ?? false) { fontName = null; @@ -126,12 +125,17 @@ class OpenTypeFont implements BinaryCodable { HeaderTable.create(glyphMetricsList, glyf, revision, unitsPerEm); final loca = useOpenType ? null - : IndexToLocationTable.create(head.indexToLocFormat, glyf); + : IndexToLocationTable.create(head.indexToLocFormat, glyf!); final hmtx = HorizontalMetricsTable.create(glyphMetricsList, unitsPerEm); final hhea = HorizontalHeaderTable.create( glyphMetricsList, hmtx, ascender, descender); final post = PostScriptTable.create(resizedGlyphList, usePostV2); final name = NamingTable.create(fontName, description, revision); + + if (name == null) { + throw TableDataFormatException('Unknown "name" table format'); + } + final maxp = MaximumProfileTable.create(fullGlyphList.length, glyf); final cmap = CharacterToGlyphTable.create(fullGlyphList); final gsub = GlyphSubstitutionTable.create(); @@ -140,13 +144,13 @@ class OpenTypeFont implements BinaryCodable { final cff = useOpenType ? CFF1Table.create(fullGlyphList, head, hmtx, name) : null; - final tables = { + final tables = { if (!useOpenType) ...{ - kGlyfTag: glyf, - kLocaTag: loca, + kGlyfTag: glyf!, + kLocaTag: loca!, }, if (useOpenType) ...{ - kCFFTag: cff, + kCFFTag: cff!, }, kCmapTag: cmap, kMaxpTag: maxp, @@ -212,7 +216,7 @@ class OpenTypeFont implements BinaryCodable { table.entry = TableRecordEntry(tag, calculateTableChecksum(encodedTable), currentTableOffset, tableSize); - entryList.add(table.entry); + entryList.add(table.entry!); currentTableOffset += getPaddedTableSize(tableSize); } @@ -231,7 +235,7 @@ class OpenTypeFont implements BinaryCodable { // Setting checksum for whole font in the head table final fontChecksum = calculateFontChecksum(byteData); - byteData.setUint32(head.entry.offset + 8, fontChecksum); + byteData.setUint32(head.entry!.offset + 8, fontChecksum); } int get entryListSize => kTableRecordEntryLength * tableMap.length; @@ -245,9 +249,9 @@ class OpenTypeFont implements BinaryCodable { // TODO: I don't like this. Refactor it later. Use "strategy" or something. static List _resizeAndCenter( List glyphList, { - int ascender, - int descender, - int fontHeight, + int? ascender, + int? descender, + int? fontHeight, }) { return glyphList.map((g) { if (fontHeight != null) { @@ -255,9 +259,13 @@ class OpenTypeFont implements BinaryCodable { return g.resize(fontHeight: fontHeight); } - return g - .resize(ascender: ascender, descender: descender) - .center(ascender, descender); + if (ascender != null && descender != null) { + return g + .resize(ascender: ascender, descender: descender) + .center(ascender, descender); + } + + throw ArgumentError('ascender/descender or fontHeight must not be null'); }).toList(); } diff --git a/lib/src/otf/reader.dart b/lib/src/otf/reader.dart index 9b54c1f..038fd42 100644 --- a/lib/src/otf/reader.dart +++ b/lib/src/otf/reader.dart @@ -13,8 +13,8 @@ class OTFReader { final ByteData _byteData; - OffsetTable _offsetTable; - OpenTypeFont _font; + late OffsetTable _offsetTable; + late OpenTypeFont _font; /// Tables by tags final _tableMap = {}; @@ -66,7 +66,7 @@ class OTFReader { continue; } - final table = _createTableFromEntry(entryMap[tag]); + final table = _createTableFromEntry(entry); if (table == null) { continue; @@ -76,7 +76,7 @@ class OTFReader { } } - FontTable _createTableFromEntry(TableRecordEntry entry) { + FontTable? _createTableFromEntry(TableRecordEntry entry) { switch (entry.tag) { case kHeadTag: return HeaderTable.fromByteData(_byteData, entry); @@ -118,20 +118,22 @@ class OTFReader { void _validateChecksums() { final byteDataCopy = ByteData.sublistView( Uint8List.fromList([..._byteData.buffer.asUint8List().toList()])) - ..setUint32(_font.head.entry.offset + 8, + ..setUint32(_font.head.entry!.offset + 8, 0); // Setting head table's checkSumAdjustment to 0 for (final table in _font.tableMap.values) { - final tableOffset = table.entry.offset; - final tableLength = table.entry.length; + final entry = table.entry!; + + final tableOffset = entry.offset; + final tableLength = entry.length; final tableByteData = ByteData.sublistView( byteDataCopy, tableOffset, tableOffset + tableLength); final actualChecksum = calculateTableChecksum(tableByteData); - final expectedChecksum = table.entry.checkSum; + final expectedChecksum = entry.checkSum; if (actualChecksum != expectedChecksum) { - throw ChecksumException.table(table.entry.tag); + throw ChecksumException.table(entry.tag); } } diff --git a/lib/src/otf/table/abstract.dart b/lib/src/otf/table/abstract.dart index e52dd6b..14add69 100644 --- a/lib/src/otf/table/abstract.dart +++ b/lib/src/otf/table/abstract.dart @@ -4,5 +4,5 @@ import 'table_record_entry.dart'; abstract class FontTable implements BinaryCodable { FontTable.fromTableRecordEntry(this.entry); - TableRecordEntry entry; + TableRecordEntry? entry; } diff --git a/lib/src/otf/table/cff.dart b/lib/src/otf/table/cff.dart index 2d7381f..ea946f0 100644 --- a/lib/src/otf/table/cff.dart +++ b/lib/src/otf/table/cff.dart @@ -31,10 +31,10 @@ const _kMajorVersion1 = 0x0001; const _kMajorVersion2 = 0x0002; abstract class CFFTable extends FontTable { - CFFTable.fromTableRecordEntry(TableRecordEntry entry) + CFFTable.fromTableRecordEntry(TableRecordEntry? entry) : super.fromTableRecordEntry(entry); - factory CFFTable.fromByteData(ByteData byteData, TableRecordEntry entry) { + static CFFTable? fromByteData(ByteData byteData, TableRecordEntry entry) { final major = byteData.getUint8(entry.offset); switch (major) { @@ -54,11 +54,12 @@ abstract class CFFTable extends FontTable { void _calculateEntryOffsets( List entryList, List offsetList, { - int operandIndex, - List operandIndexList, + int? operandIndex, + List? operandIndexList, }) { - assert(operandIndex != null || operandIndexList != null, - 'Specify operand index'); + if (operandIndex == null && operandIndexList == null) { + throw ArgumentError.notNull('Specify operand index'); + } bool sizeChanged; @@ -68,7 +69,7 @@ void _calculateEntryOffsets( sizeChanged = false; for (var i = 0; i < entryList.length; i++) { - final entryOperandIndex = operandIndex ?? operandIndexList[i]; + final entryOperandIndex = operandIndex ?? operandIndexList![i]; final entry = entryList[i]; final oldOperand = entry.operandList[entryOperandIndex]; final newOperand = CFFOperand.fromValue(offsetList[i]); diff --git a/lib/src/otf/table/cff1.dart b/lib/src/otf/table/cff1.dart index 187cc9f..0ef9a29 100644 --- a/lib/src/otf/table/cff1.dart +++ b/lib/src/otf/table/cff1.dart @@ -27,7 +27,7 @@ class CFF1TableHeader implements BinaryCodable { final int majorVersion; final int minorVersion; final int headerSize; - int offSize; + int? offSize; @override void encodeToBinary(ByteData byteData) { @@ -35,7 +35,7 @@ class CFF1TableHeader implements BinaryCodable { ..setUint8(0, majorVersion) ..setUint8(1, minorVersion) ..setUint8(2, headerSize) - ..setUint8(3, offSize); + ..setUint8(3, offSize!); } @override @@ -44,7 +44,7 @@ class CFF1TableHeader implements BinaryCodable { class CFF1Table extends CFFTable implements CalculatableOffsets { CFF1Table( - TableRecordEntry entry, + TableRecordEntry? entry, this.header, this.nameIndex, this.topDicts, @@ -94,10 +94,10 @@ class CFF1Table extends CFFTable implements CalculatableOffsets { byteData.sublistView(fixedOffset), true, ); - fixedOffset += globalSubrsData.index.size; + fixedOffset += globalSubrsData.index!.size; /// CharStrings INDEX - final charStringsIndexEntry = topDict.getEntryForOperator(op.charStrings); + final charStringsIndexEntry = topDict.getEntryForOperator(op.charStrings)!; final charStringsIndexOffset = charStringsIndexEntry.operandList.first.value as int; final charStringsIndexByteData = @@ -110,16 +110,16 @@ class CFF1Table extends CFFTable implements CalculatableOffsets { /// Charsets final charsetsOffset = - topDict.getEntryForOperator(op.charset).operandList.first.value as int; + topDict.getEntryForOperator(op.charset)!.operandList.first.value as int; final charsetsByteData = byteData.sublistView(entry.offset + charsetsOffset); final charsetEntry = CharsetEntry.fromByteData( charsetsByteData, - charStringsData.index.count, - ); + charStringsData.index!.count, + )!; - final privateEntry = topDict.getEntryForOperator(op.private); + final privateEntry = topDict.getEntryForOperator(op.private)!; final dictOffset = entry.offset + (privateEntry.operandList.last.value as int); final dictLength = privateEntry.operandList.first.value as int; @@ -186,13 +186,13 @@ class CFF1Table extends CFFTable implements CalculatableOffsets { if (standardSid != null) { sidList.add(standardSid); } else { - putStringInIndex(g.metadata.name); + putStringInIndex(g.metadata.name!); } } final glyphSidList = [...sidList]; - final fontName = name.getStringByNameId(NameID.fullFontName); + final fontName = name.getStringByNameId(NameID.fullFontName)!; final copyrightString = '${name.getStringByNameId(NameID.copyright)} ' '${name.getStringByNameId(NameID.urlVendor)}'; @@ -206,10 +206,11 @@ class CFF1Table extends CFFTable implements CalculatableOffsets { final topDicts = CFFIndexWithData.create([ CFFDict([ for (final e in topDictStringEntryMap.entries) - CFFDictEntry( - [CFFOperand.fromValue(putStringInIndex(e.value))], - e.key, - ), + if (e.value != null) + CFFDictEntry( + [CFFOperand.fromValue(putStringInIndex(e.value!))], + e.key, + ), CFFDictEntry([ CFFOperand.fromValue(head.xMin), CFFOperand.fromValue(head.yMin), @@ -330,9 +331,9 @@ class CFF1Table extends CFFTable implements CalculatableOffsets { final privateDictOffset = offset; offset += privateDict.size; - final charsetEntry = topDict.getEntryForOperator(op.charset); - final charStringsEntry = topDict.getEntryForOperator(op.charStrings); - final privateEntry = topDict.getEntryForOperator(op.private); + final charsetEntry = topDict.getEntryForOperator(op.charset)!; + final charStringsEntry = topDict.getEntryForOperator(op.charStrings)!; + final privateEntry = topDict.getEntryForOperator(op.private)!; final offsetList = [ charsetOffset, @@ -363,7 +364,7 @@ class CFF1Table extends CFFTable implements CalculatableOffsets { localSubrsDataList.forEach((e) => e.recalculateOffsets()); // Last data offset - final lastDataEntry = topDict.getEntryForOperator(op.private); + final lastDataEntry = topDict.getEntryForOperator(op.private)!; final lastDataOffset = lastDataEntry.operandList.last.value as int; header.offSize = (lastDataOffset.bitLength / 8).ceil(); } diff --git a/lib/src/otf/table/cff2.dart b/lib/src/otf/table/cff2.dart index 98f05f9..5cbd0cf 100644 --- a/lib/src/otf/table/cff2.dart +++ b/lib/src/otf/table/cff2.dart @@ -21,7 +21,7 @@ class CFF2TableHeader implements BinaryCodable { final int majorVersion; final int minorVersion; final int headerSize; - int topDictLength; + int? topDictLength; @override void encodeToBinary(ByteData byteData) { @@ -29,7 +29,7 @@ class CFF2TableHeader implements BinaryCodable { ..setUint8(0, majorVersion) ..setUint8(1, minorVersion) ..setUint8(2, headerSize) - ..setUint16(3, topDictLength); + ..setUint16(3, topDictLength!); } @override @@ -38,7 +38,7 @@ class CFF2TableHeader implements BinaryCodable { class CFF2Table extends CFFTable implements CalculatableOffsets { CFF2Table( - TableRecordEntry entry, + TableRecordEntry? entry, this.header, this.topDict, this.globalSubrsData, @@ -61,15 +61,15 @@ class CFF2Table extends CFFTable implements CalculatableOffsets { fixedOffset += _kCFF2HeaderSize; final topDict = CFFDict.fromByteData( - byteData.sublistView(fixedOffset, header.topDictLength)); - fixedOffset += header.topDictLength; + byteData.sublistView(fixedOffset, header.topDictLength!)); + fixedOffset += header.topDictLength!; final globalSubrsData = CFFIndexWithData.fromByteData( byteData.sublistView(fixedOffset), false); - fixedOffset += globalSubrsData.index.size; + fixedOffset += globalSubrsData.index!.size; /// CharStrings INDEX - final charStringsIndexEntry = topDict.getEntryForOperator(op.charStrings); + final charStringsIndexEntry = topDict.getEntryForOperator(op.charStrings)!; final charStringsIndexOffset = charStringsIndexEntry.operandList.first.value as int; final charStringsIndexByteData = @@ -80,7 +80,7 @@ class CFF2Table extends CFFTable implements CalculatableOffsets { /// VariationStore final vstoreEntry = topDict.getEntryForOperator(op.vstore); - VariationStoreData vstoreData; + VariationStoreData? vstoreData; if (vstoreEntry != null) { final vstoreOffset = vstoreEntry.operandList.first.value as int; @@ -91,7 +91,7 @@ class CFF2Table extends CFFTable implements CalculatableOffsets { // NOTE: not decoding FDSelect - using single Font DICT only /// Font DICT INDEX - final fdArrayEntry = topDict.getEntryForOperator(op.fdArray); + final fdArrayEntry = topDict.getEntryForOperator(op.fdArray)!; final fdArrayOffset = fdArrayEntry.operandList.first.value as int; final fontIndexByteData = @@ -107,8 +107,9 @@ class CFF2Table extends CFFTable implements CalculatableOffsets { /// Local subroutines for each Private DICT final localSubrsDataList = >[]; - for (var i = 0; i < fontDictList.index.count; i++) { - final privateEntry = fontDictList.data[i].getEntryForOperator(op.private); + for (var i = 0; i < fontDictList.index!.count; i++) { + final privateEntry = + fontDictList.data[i].getEntryForOperator(op.private)!; final dictOffset = entry.offset + (privateEntry.operandList.last.value as int); final dictLength = privateEntry.operandList.first.value as int; @@ -140,7 +141,7 @@ class CFF2Table extends CFFTable implements CalculatableOffsets { final header = CFF2TableHeader.create(); final topDict = CFFDict.empty(); final globalSubrsData = CFFIndexWithData.create([], false); - const VariationStoreData vstoreData = null; // omitted - no variations + const VariationStoreData? vstoreData = null; // omitted - no variations final charStringInterpreter = CharStringInterpreter(false); @@ -190,7 +191,7 @@ class CFF2Table extends CFFTable implements CalculatableOffsets { final CFFDict topDict; final CFFIndexWithData globalSubrsData; final CFFIndexWithData charStringsData; - final VariationStoreData vstoreData; + final VariationStoreData? vstoreData; final CFFIndexWithData fontDictList; final List privateDictList; final List> localSubrsDataList; @@ -213,10 +214,10 @@ class CFF2Table extends CFFTable implements CalculatableOffsets { var offset = header.size + globalSubrsData.size + topDict.size; - int vstoreOffset; + int? vstoreOffset; if (vstoreData != null) { vstoreOffset = offset; - offset += vstoreData.size; + offset += vstoreData!.size; } final charStringsOffset = offset; @@ -226,11 +227,11 @@ class CFF2Table extends CFFTable implements CalculatableOffsets { offset += fontDictList.size; final vstoreEntry = topDict.getEntryForOperator(op.vstore); - final charStringsEntry = topDict.getEntryForOperator(op.charStrings); - final fdArrayEntry = topDict.getEntryForOperator(op.fdArray); + final charStringsEntry = topDict.getEntryForOperator(op.charStrings)!; + final fdArrayEntry = topDict.getEntryForOperator(op.fdArray)!; final offsetList = [ - if (vstoreData != null) vstoreOffset, + if (vstoreData != null) vstoreOffset!, charStringsOffset, fdArrayOffset, ]; @@ -255,15 +256,15 @@ class CFF2Table extends CFFTable implements CalculatableOffsets { charStringsData.recalculateOffsets(); // Recalculating font DICTs private offsets and SUBRS entries offsets - final fdArrayEntry = topDict.getEntryForOperator(op.fdArray); + final fdArrayEntry = topDict.getEntryForOperator(op.fdArray)!; final fdArrayOffset = fdArrayEntry.operandList.first.value as int; - var fontDictOffset = fdArrayOffset + fontDictList.index.size; + var fontDictOffset = fdArrayOffset + fontDictList.index!.size; for (var i = 0; i < fontDictList.data.length; i++) { final fontDict = fontDictList.data[i]; final privateDict = privateDictList[i]; - final privateEntry = fontDict.getEntryForOperator(op.private); + final privateEntry = fontDict.getEntryForOperator(op.private)!; final newOperands = [ CFFOperand.fromValue(privateDict.size), @@ -308,8 +309,8 @@ class CFF2Table extends CFFTable implements CalculatableOffsets { offset += globalSubrsSize; if (vstoreData != null) { - final vstoreSize = vstoreData.size; - vstoreData.encodeToBinary(byteData.sublistView(offset, vstoreSize)); + final vstoreSize = vstoreData!.size; + vstoreData!.encodeToBinary(byteData.sublistView(offset, vstoreSize)); offset += vstoreSize; } diff --git a/lib/src/otf/table/cmap.dart b/lib/src/otf/table/cmap.dart index 340bc8d..f0c2171 100644 --- a/lib/src/otf/table/cmap.dart +++ b/lib/src/otf/table/cmap.dart @@ -71,7 +71,7 @@ class EncodingRecord implements BinaryCodable { final int platformID; final int encodingID; - int offset; + int? offset; @override int get size => _kEncodingRecordSize; @@ -81,7 +81,7 @@ class EncodingRecord implements BinaryCodable { byteData ..setUint16(0, platformID) ..setUint16(2, encodingID) - ..setUint32(4, offset); + ..setUint32(4, offset!); } } @@ -153,7 +153,7 @@ class CharacterToGlyphTableHeader implements BinaryCodable { abstract class CmapData implements BinaryCodable { CmapData(this.format); - factory CmapData.fromByteData(ByteData byteData, int offset) { + static CmapData? fromByteData(ByteData byteData, int offset) { final format = byteData.getUint16(offset); switch (format) { @@ -170,7 +170,7 @@ abstract class CmapData implements BinaryCodable { } } - factory CmapData.create(List<_Segment> segmentList, int format) { + static CmapData? create(List<_Segment> segmentList, int format) { switch (format) { case _kFormat0: return CmapByteEncodingTable.create(); @@ -462,7 +462,7 @@ class CmapSegmentedCoverageTable extends CmapData { class CharacterToGlyphTable extends FontTable { CharacterToGlyphTable( - TableRecordEntry entry, + TableRecordEntry? entry, this.header, this.data, ) : super.fromTableRecordEntry(entry); @@ -473,16 +473,21 @@ class CharacterToGlyphTable extends FontTable { ) { final header = CharacterToGlyphTableHeader.fromByteData(byteData, entry); final data = List.generate( - header.numTables, - (i) => CmapData.fromByteData( - byteData, entry.offset + header.encodingRecords[i].offset)); + header.numTables, + (i) => CmapData.fromByteData( + byteData, entry.offset + header.encodingRecords[i].offset!)) + .whereType() + .toList(); return CharacterToGlyphTable(entry, header, data); } factory CharacterToGlyphTable.create(List fullGlyphList) { - final charCodeList = fullGlyphList.map((e) => e.metadata.charCode).toList() - ..removeAt(0); // removing .notdef + final fullCharCodeList = fullGlyphList + .map((e) => e.metadata.charCode) + .toList() + ..removeAt(0); // removing .notdef + final charCodeList = fullCharCodeList.whereType().toList(); final segmentList = _generateSegments(charCodeList); final segmentListFormat4 = [ @@ -493,7 +498,7 @@ class CharacterToGlyphTable extends FontTable { final subtableByFormat = _kDefaultEncodingRecordFormatList .toSet() - .fold>({}, (p, format) { + .fold>({}, (p, format) { p[format] = CmapData.create( format == _kFormat4 ? segmentListFormat4 : segmentList, format); return p; @@ -501,7 +506,7 @@ class CharacterToGlyphTable extends FontTable { final subtables = [ for (final format in _kDefaultEncodingRecordFormatList) - subtableByFormat[format] + if (subtableByFormat[format] != null) subtableByFormat[format]!, ]; final header = CharacterToGlyphTableHeader( diff --git a/lib/src/otf/table/coverage.dart b/lib/src/otf/table/coverage.dart index a872330..48b2ebb 100644 --- a/lib/src/otf/table/coverage.dart +++ b/lib/src/otf/table/coverage.dart @@ -8,7 +8,7 @@ const kDefaultCoverageTable = CoverageTableFormat1(1, 0, []); abstract class CoverageTable implements BinaryCodable { const CoverageTable(); - factory CoverageTable.fromByteData(ByteData byteData, int offset) { + static CoverageTable? fromByteData(ByteData byteData, int offset) { final format = byteData.getUint16(offset); switch (format) { diff --git a/lib/src/otf/table/feature_list.dart b/lib/src/otf/table/feature_list.dart index 32e24d7..a418716 100644 --- a/lib/src/otf/table/feature_list.dart +++ b/lib/src/otf/table/feature_list.dart @@ -24,7 +24,7 @@ class FeatureRecord implements BinaryCodable { } final String featureTag; - int featureOffset; + int? featureOffset; @override int get size => kFeatureRecordSize; @@ -33,7 +33,7 @@ class FeatureRecord implements BinaryCodable { void encodeToBinary(ByteData byteData) { byteData ..setTag(0, featureTag) - ..setUint16(4, featureOffset); + ..setUint16(4, featureOffset!); } } @@ -43,7 +43,7 @@ class FeatureTable implements BinaryCodable { factory FeatureTable.fromByteData( ByteData byteData, int offset, FeatureRecord record) { - offset += record.featureOffset; + offset += record.featureOffset!; final lookupIndexCount = byteData.getUint16(offset + 2); final lookupListIndices = List.generate( diff --git a/lib/src/otf/table/glyf.dart b/lib/src/otf/table/glyf.dart index 82eefdb..07e41ac 100644 --- a/lib/src/otf/table/glyf.dart +++ b/lib/src/otf/table/glyf.dart @@ -12,7 +12,7 @@ import 'table_record_entry.dart'; class GlyphDataTable extends FontTable { GlyphDataTable( - TableRecordEntry entry, + TableRecordEntry? entry, this.glyphList, ) : super.fromTableRecordEntry(entry); diff --git a/lib/src/otf/table/glyph/flag.dart b/lib/src/otf/table/glyph/flag.dart index d262cd3..2360232 100644 --- a/lib/src/otf/table/glyph/flag.dart +++ b/lib/src/otf/table/glyph/flag.dart @@ -23,7 +23,7 @@ class SimpleGlyphFlag implements BinaryCodable { this.overlapSimple, this.reserved); - factory SimpleGlyphFlag.fromIntValue(int flag, [int repeatTimes]) { + factory SimpleGlyphFlag.fromIntValue(int flag, [int? repeatTimes]) { return SimpleGlyphFlag( checkBitMask(flag, _kOnCurvePointValue), checkBitMask(flag, _kXshortVectorValue), @@ -61,7 +61,7 @@ class SimpleGlyphFlag implements BinaryCodable { final bool onCurvePoint; final bool xShortVector; final bool yShortVector; - final int repeat; + final int? repeat; final bool xIsSameOrPositive; final bool yIsSameOrPositive; final bool overlapSimple; diff --git a/lib/src/otf/table/gsub.dart b/lib/src/otf/table/gsub.dart index 69927bb..503fac6 100644 --- a/lib/src/otf/table/gsub.dart +++ b/lib/src/otf/table/gsub.dart @@ -48,10 +48,10 @@ class GlyphSubstitutionTableHeader implements BinaryCodable { final int majorVersion; final int minorVersion; - int scriptListOffset; - int featureListOffset; - int lookupListOffset; - int featureVariationsOffset; + int? scriptListOffset; + int? featureListOffset; + int? lookupListOffset; + int? featureVariationsOffset; bool get isV10 => majorVersion == 1 && minorVersion == 0; @@ -63,9 +63,9 @@ class GlyphSubstitutionTableHeader implements BinaryCodable { byteData ..setUint16(0, majorVersion) ..setUint16(2, minorVersion) - ..setUint16(4, scriptListOffset) - ..setUint16(6, featureListOffset) - ..setUint16(8, lookupListOffset); + ..setUint16(4, scriptListOffset!) + ..setUint16(6, featureListOffset!) + ..setUint16(8, lookupListOffset!); if (!isV10) { byteData.getUint32(10); @@ -74,7 +74,7 @@ class GlyphSubstitutionTableHeader implements BinaryCodable { } class GlyphSubstitutionTable extends FontTable { - GlyphSubstitutionTable(TableRecordEntry entry, this.header, + GlyphSubstitutionTable(TableRecordEntry? entry, this.header, this.scriptListTable, this.featureListTable, this.lookupListTable) : super.fromTableRecordEntry(entry); @@ -85,11 +85,11 @@ class GlyphSubstitutionTable extends FontTable { final header = GlyphSubstitutionTableHeader.fromByteData(byteData, entry); final scriptListTable = ScriptListTable.fromByteData( - byteData, entry.offset + header.scriptListOffset); + byteData, entry.offset + header.scriptListOffset!); final featureListTable = FeatureListTable.fromByteData( - byteData, entry.offset + header.featureListOffset); + byteData, entry.offset + header.featureListOffset!); final lookupListTable = LookupListTable.fromByteData( - byteData, entry.offset + header.lookupListOffset); + byteData, entry.offset + header.lookupListOffset!); return GlyphSubstitutionTable( entry, diff --git a/lib/src/otf/table/head.dart b/lib/src/otf/table/head.dart index 98d7a56..5451c30 100644 --- a/lib/src/otf/table/head.dart +++ b/lib/src/otf/table/head.dart @@ -2,7 +2,6 @@ import 'dart:math' as math; import 'dart:typed_data'; import '../../common/generic_glyph.dart'; -import '../../utils/exception.dart'; import '../../utils/misc.dart'; import '../../utils/otf.dart'; @@ -23,7 +22,7 @@ const _kHeaderTableSize = 54; class HeaderTable extends FontTable { HeaderTable( - TableRecordEntry entry, + TableRecordEntry? entry, this.fontRevision, this.checkSumAdjustment, this.flags, @@ -90,15 +89,10 @@ class HeaderTable extends FontTable { factory HeaderTable.create( List glyphMetricsList, - GlyphDataTable glyf, + GlyphDataTable? glyf, Revision revision, int unitsPerEm, ) { - if (revision == null || revision.int32value == 0) { - throw TableDataFormatException('revision must not be null'); - } - - final isOpenType = glyf == null; final now = MockableDateTime.now(); final xMin = glyphMetricsList.fold( @@ -124,7 +118,7 @@ class HeaderTable extends FontTable { yMax, _kMacStyleRegular, _kLowestRecPPEMdefault, - isOpenType || glyf.size < 0x20000 + glyf == null || glyf.size < 0x20000 ? _kIndexToLocFormatShort : _kIndexToLocFormatLong); } diff --git a/lib/src/otf/table/hhea.dart b/lib/src/otf/table/hhea.dart index b26ed76..c8b5ff9 100644 --- a/lib/src/otf/table/hhea.dart +++ b/lib/src/otf/table/hhea.dart @@ -10,7 +10,7 @@ const _kHheaTableSize = 36; class HorizontalHeaderTable extends FontTable { HorizontalHeaderTable( - TableRecordEntry entry, + TableRecordEntry? entry, this.majorVersion, this.minorVersion, this.ascender, diff --git a/lib/src/otf/table/hmtx.dart b/lib/src/otf/table/hmtx.dart index a3888f4..0878398 100644 --- a/lib/src/otf/table/hmtx.dart +++ b/lib/src/otf/table/hmtx.dart @@ -49,7 +49,7 @@ class LongHorMetric implements BinaryCodable { class HorizontalMetricsTable extends FontTable { HorizontalMetricsTable( - TableRecordEntry entry, + TableRecordEntry? entry, this.hMetrics, this.leftSideBearings, ) : super.fromTableRecordEntry(entry); diff --git a/lib/src/otf/table/loca.dart b/lib/src/otf/table/loca.dart index 32f6649..cc91016 100644 --- a/lib/src/otf/table/loca.dart +++ b/lib/src/otf/table/loca.dart @@ -7,7 +7,7 @@ import 'table_record_entry.dart'; class IndexToLocationTable extends FontTable { IndexToLocationTable( - TableRecordEntry entry, + TableRecordEntry? entry, this.glyphOffsets, this._isShort, ) : super.fromTableRecordEntry(entry); diff --git a/lib/src/otf/table/lookup.dart b/lib/src/otf/table/lookup.dart index 9883b64..336f6d8 100644 --- a/lib/src/otf/table/lookup.dart +++ b/lib/src/otf/table/lookup.dart @@ -18,7 +18,7 @@ const _kDefaultLookupTableList = [ abstract class SubstitutionSubtable implements BinaryCodable { const SubstitutionSubtable(); - factory SubstitutionSubtable.fromByteData( + static SubstitutionSubtable? fromByteData( ByteData byteData, int offset, int lookupType) { switch (lookupType) { case 4: @@ -65,10 +65,10 @@ class LigatureSubstitutionSubtable extends SubstitutionSubtable { final int ligatureSetCount; final List ligatureSetOffsets; - final CoverageTable coverageTable; + final CoverageTable? coverageTable; @override - int get size => 6 + 2 * ligatureSetCount + coverageTable.size; + int get size => 6 + 2 * ligatureSetCount + (coverageTable?.size ?? 0); /// NOTE: Should be calculated considering 'componentCount' of ligatures. /// @@ -89,8 +89,8 @@ class LigatureSubstitutionSubtable extends SubstitutionSubtable { byteData.setInt16(6 + 2 * i, ligatureSetOffsets[i]); } - coverageTable.encodeToBinary( - byteData.sublistView(coverageOffset, coverageTable.size)); + coverageTable?.encodeToBinary( + byteData.sublistView(coverageOffset, coverageTable!.size)); } } @@ -114,9 +114,11 @@ class LookupTable implements BinaryCodable { final markFilteringSetOffset = offset + 6 + 2 * subTableCount; final subtables = List.generate( - subTableCount, - (i) => SubstitutionSubtable.fromByteData( - byteData, offset + subtableOffsets[i], lookupType)); + subTableCount, + (i) => SubstitutionSubtable.fromByteData( + byteData, offset + subtableOffsets[i], lookupType)) + .whereType() + .toList(); return LookupTable( lookupType, @@ -132,7 +134,7 @@ class LookupTable implements BinaryCodable { final int lookupFlag; final int subTableCount; final List subtableOffsets; - final int markFilteringSet; + final int? markFilteringSet; final List subtables; @@ -170,7 +172,7 @@ class LookupTable implements BinaryCodable { final useMarkFilteringSet = _useMarkFilteringSet(lookupFlag); if (useMarkFilteringSet) { - byteData.setUint16(6 + 2 * subTableCount, markFilteringSet); + byteData.setUint16(6 + 2 * subTableCount, markFilteringSet!); } } } diff --git a/lib/src/otf/table/maxp.dart b/lib/src/otf/table/maxp.dart index bdbfa5c..61c6d8a 100644 --- a/lib/src/otf/table/maxp.dart +++ b/lib/src/otf/table/maxp.dart @@ -16,7 +16,7 @@ const _kTableSizeForVersion = { }; class MaximumProfileTable extends FontTable { - MaximumProfileTable.v0(TableRecordEntry entry, this.numGlyphs) + MaximumProfileTable.v0(TableRecordEntry? entry, this.numGlyphs) : version = _kVersion0, maxPoints = null, maxContours = null, @@ -34,7 +34,7 @@ class MaximumProfileTable extends FontTable { super.fromTableRecordEntry(entry); MaximumProfileTable.v1( - TableRecordEntry entry, + TableRecordEntry? entry, this.numGlyphs, this.maxPoints, this.maxContours, @@ -52,7 +52,34 @@ class MaximumProfileTable extends FontTable { : version = _kVersion1, super.fromTableRecordEntry(entry); - factory MaximumProfileTable.fromByteData( + factory MaximumProfileTable.create(int numGlyphs, GlyphDataTable? glyf) { + final isOpenType = glyf == null; + + if (isOpenType) { + return MaximumProfileTable.v0(null, numGlyphs); + } + + return MaximumProfileTable.v1( + null, + numGlyphs, + glyf!.maxPoints, + glyf.maxContours, + 0, // Composite glyphs are not supported + 0, // Composite glyphs are not supported + 2, // The twilight zone is used + 0, // 0 max points for the twilight zone + + /// Constants taken from FontForge + 1, + 1, + 0, + 64, + glyf.maxSizeOfInstructions, + 0, + 0); + } + + static MaximumProfileTable? fromByteData( ByteData data, TableRecordEntry entry) { final version = data.getInt32(entry.offset); @@ -83,51 +110,24 @@ class MaximumProfileTable extends FontTable { } } - factory MaximumProfileTable.create(int numGlyphs, GlyphDataTable glyf) { - final isOpenType = glyf == null; - - if (isOpenType) { - return MaximumProfileTable.v0(null, numGlyphs); - } - - return MaximumProfileTable.v1( - null, - numGlyphs, - glyf.maxPoints, - glyf.maxContours, - 0, // Composite glyphs are not supported - 0, // Composite glyphs are not supported - 2, // The twilight zone is used - 0, // 0 max points for the twilight zone - - /// Constants taken from FontForge - 1, - 1, - 0, - 64, - glyf.maxSizeOfInstructions, - 0, - 0); - } - // Version 0.5 final int version; final int numGlyphs; // Version 1.0 - final int maxPoints; - final int maxContours; - final int maxCompositePoints; - final int maxCompositeContours; - final int maxZones; - final int maxTwilightPoints; - final int maxStorage; - final int maxFunctionDefs; - final int maxInstructionDefs; - final int maxStackElements; - final int maxSizeOfInstructions; - final int maxComponentElements; - final int maxComponentDepth; + final int? maxPoints; + final int? maxContours; + final int? maxCompositePoints; + final int? maxCompositeContours; + final int? maxZones; + final int? maxTwilightPoints; + final int? maxStorage; + final int? maxFunctionDefs; + final int? maxInstructionDefs; + final int? maxStackElements; + final int? maxSizeOfInstructions; + final int? maxComponentElements; + final int? maxComponentDepth; @override void encodeToBinary(ByteData byteData) { @@ -137,24 +137,24 @@ class MaximumProfileTable extends FontTable { if (version == _kVersion1) { byteData - ..setUint16(6, maxPoints) - ..setUint16(8, maxContours) - ..setUint16(10, maxCompositePoints) - ..setUint16(12, maxCompositeContours) - ..setUint16(14, maxZones) - ..setUint16(16, maxTwilightPoints) - ..setUint16(18, maxStorage) - ..setUint16(20, maxFunctionDefs) - ..setUint16(22, maxInstructionDefs) - ..setUint16(24, maxStackElements) - ..setUint16(26, maxSizeOfInstructions) - ..setUint16(28, maxComponentElements) - ..setUint16(30, maxComponentDepth); + ..setUint16(6, maxPoints!) + ..setUint16(8, maxContours!) + ..setUint16(10, maxCompositePoints!) + ..setUint16(12, maxCompositeContours!) + ..setUint16(14, maxZones!) + ..setUint16(16, maxTwilightPoints!) + ..setUint16(18, maxStorage!) + ..setUint16(20, maxFunctionDefs!) + ..setUint16(22, maxInstructionDefs!) + ..setUint16(24, maxStackElements!) + ..setUint16(26, maxSizeOfInstructions!) + ..setUint16(28, maxComponentElements!) + ..setUint16(30, maxComponentDepth!); } else if (version != _kVersion0) { OTFDebugger.debugUnsupportedTableVersion(kMaxpTag, version); } } @override - int get size => _kTableSizeForVersion[version]; + int get size => _kTableSizeForVersion[version]!; } diff --git a/lib/src/otf/table/name.dart b/lib/src/otf/table/name.dart index 1dd0829..703e9d1 100644 --- a/lib/src/otf/table/name.dart +++ b/lib/src/otf/table/name.dart @@ -3,7 +3,6 @@ import 'dart:typed_data'; import '../../common/codable/binary.dart'; import '../../common/constant.dart'; import '../../utils/enum_class.dart'; -import '../../utils/exception.dart'; import '../../utils/otf.dart'; import '../../utils/ucs2.dart'; import '../debugger.dart'; @@ -107,9 +106,9 @@ class NameRecord implements BinaryCodable { this.platformID, this.encodingID, this.languageID, - ) : nameID = null, - length = null, - offset = null; + ) : nameID = -1, + length = -1, + offset = -1; factory NameRecord.fromByteData(ByteData byteData, int offset) { final length = byteData.getUint16(offset + 8); @@ -133,20 +132,21 @@ class NameRecord implements BinaryCodable { final int offset; NameRecord copyWith({ - int platformID, - int encodingID, - int languageID, - int nameID, - int length, - int offset, + int? platformID, + int? encodingID, + int? languageID, + int? nameID, + int? length, + int? offset, }) { return NameRecord( - this.platformID ?? platformID, - this.encodingID ?? encodingID, - this.languageID ?? languageID, - this.nameID ?? nameID, - this.length ?? length, - this.offset ?? offset); + platformID ?? this.platformID, + encodingID ?? this.encodingID, + languageID ?? this.languageID, + nameID ?? this.nameID, + length ?? this.length, + offset ?? this.offset, + ); } @override @@ -172,7 +172,12 @@ class NamingTableFormat0Header implements BinaryCodable { this.nameRecordList, ); - factory NamingTableFormat0Header.fromByteData( + factory NamingTableFormat0Header.create(List nameRecordList) { + return NamingTableFormat0Header(_kFormat0, nameRecordList.length, + 6 + nameRecordList.length * _kNameRecordSize, nameRecordList); + } + + static NamingTableFormat0Header? fromByteData( ByteData byteData, TableRecordEntry entry, ) { @@ -193,11 +198,6 @@ class NamingTableFormat0Header implements BinaryCodable { return NamingTableFormat0Header(format, count, stringOffset, nameRecord); } - factory NamingTableFormat0Header.create(List nameRecordList) { - return NamingTableFormat0Header(_kFormat0, nameRecordList.length, - 6 + nameRecordList.length * _kNameRecordSize, nameRecordList); - } - final int format; final int count; final int stringOffset; @@ -223,10 +223,10 @@ class NamingTableFormat0Header implements BinaryCodable { } abstract class NamingTable extends FontTable { - NamingTable.fromTableRecordEntry(TableRecordEntry entry) + NamingTable.fromTableRecordEntry(TableRecordEntry? entry) : super.fromTableRecordEntry(entry); - factory NamingTable.fromByteData(ByteData byteData, TableRecordEntry entry) { + static NamingTable? fromByteData(ByteData byteData, TableRecordEntry entry) { final format = byteData.getUint16(entry.offset); switch (format) { @@ -238,8 +238,8 @@ abstract class NamingTable extends FontTable { } } - factory NamingTable.create( - String fontName, String description, Revision revision, + static NamingTable? create( + String fontName, String? description, Revision revision, {int format = _kFormat0}) { switch (format) { case _kFormat0: @@ -252,40 +252,22 @@ abstract class NamingTable extends FontTable { String get familyName; - String getStringByNameId(NameID nameId); + String? getStringByNameId(NameID nameId); } class NamingTableFormat0 extends NamingTable { NamingTableFormat0( - TableRecordEntry entry, + TableRecordEntry? entry, this.header, this.stringList, ) : super.fromTableRecordEntry(entry); - factory NamingTableFormat0.fromByteData( - ByteData byteData, TableRecordEntry entry) { - final header = NamingTableFormat0Header.fromByteData(byteData, entry); - final storageAreaOffset = entry.offset + header.size; - - final stringList = [ - for (final record in header.nameRecordList) - _getDecoder(record)(List.generate(record.length, - (i) => byteData.getUint8(storageAreaOffset + record.offset + i))) - ]; - - return NamingTableFormat0(entry, header, stringList); - } - factory NamingTableFormat0.create( - String fontName, String description, Revision revision) { - if (fontName?.isNotEmpty != true) { - throw TableDataFormatException('Font name must be not empty'); - } - + String fontName, String? description, Revision revision) { final now = DateTime.now(); /// Values for name ids in sorted order - final stringForNameMap = { + final stringForNameMap = { NameID.copyright: 'Copyright $kVendorName ${now.year}', NameID.fontFamily: fontName, NameID.fontSubfamily: 'Regular', @@ -328,6 +310,25 @@ class NamingTableFormat0 extends NamingTable { return NamingTableFormat0(null, header, stringList); } + static NamingTableFormat0? fromByteData( + ByteData byteData, TableRecordEntry entry) { + final header = NamingTableFormat0Header.fromByteData(byteData, entry); + + if (header == null) { + return null; + } + + final storageAreaOffset = entry.offset + header.size; + + final stringList = [ + for (final record in header.nameRecordList) + _getDecoder(record)(List.generate(record.length, + (i) => byteData.getUint8(storageAreaOffset + record.offset + i))) + ]; + + return NamingTableFormat0(entry, header, stringList); + } + final NamingTableFormat0Header header; final List stringList; @@ -356,10 +357,10 @@ class NamingTableFormat0 extends NamingTable { } @override - String get familyName => getStringByNameId(NameID.fontFamily); + String get familyName => getStringByNameId(NameID.fontFamily)!; @override - String getStringByNameId(NameID nameId) { + String? getStringByNameId(NameID nameId) { final nameID = _kNameIDmap.getValueForKey(nameId); final familyIndex = header.nameRecordList.indexWhere((e) => e.nameID == nameID); diff --git a/lib/src/otf/table/os2.dart b/lib/src/otf/table/os2.dart index 0a5c12a..8bc7fc4 100644 --- a/lib/src/otf/table/os2.dart +++ b/lib/src/otf/table/os2.dart @@ -45,7 +45,7 @@ const _kDefaultPANOSE = [2, 0, 5, 3, 0, 0, 0, 0, 0, 0]; class OS2Table extends FontTable { OS2Table._( - TableRecordEntry entry, + TableRecordEntry? entry, this.version, this.xAvgCharWidth, this.usWeightClass, @@ -151,9 +151,9 @@ class OS2Table extends FontTable { String achVendID, { int version = _kVersion5, }) { - final asciiAchVendID = achVendID?.getAsciiPrintable(); + final asciiAchVendID = achVendID.getAsciiPrintable(); - if (asciiAchVendID?.length != 4) { + if (asciiAchVendID.length != 4) { throw TableDataFormatException( 'Incorrect achVendID tag format in OS/2 table'); } @@ -263,19 +263,19 @@ class OS2Table extends FontTable { final int usWinDescent; // Version 1 - final int ulCodePageRange1; - final int ulCodePageRange2; + final int? ulCodePageRange1; + final int? ulCodePageRange2; // Version 4 - final int sxHeight; - final int sCapHeight; - final int usDefaultChar; - final int usBreakChar; - final int usMaxContext; + final int? sxHeight; + final int? sCapHeight; + final int? usDefaultChar; + final int? usBreakChar; + final int? usMaxContext; // Version 5 - final int usLowerOpticalPointSize; - final int usUpperOpticalPointSize; + final int? usLowerOpticalPointSize; + final int? usUpperOpticalPointSize; @override int get size { @@ -367,23 +367,23 @@ class OS2Table extends FontTable { if (isV1) { byteData - ..setUint32(78, ulCodePageRange1) - ..setUint32(82, ulCodePageRange2); + ..setUint32(78, ulCodePageRange1!) + ..setUint32(82, ulCodePageRange2!); } if (isV4) { byteData - ..setInt16(86, sxHeight) - ..setInt16(88, sCapHeight) - ..setUint16(90, usDefaultChar) - ..setUint16(92, usBreakChar) - ..setUint16(94, usMaxContext); + ..setInt16(86, sxHeight!) + ..setInt16(88, sCapHeight!) + ..setUint16(90, usDefaultChar!) + ..setUint16(92, usBreakChar!) + ..setUint16(94, usMaxContext!); } if (isV5) { byteData - ..setUint16(96, usLowerOpticalPointSize) - ..setUint16(98, usUpperOpticalPointSize); + ..setUint16(96, usLowerOpticalPointSize!) + ..setUint16(98, usUpperOpticalPointSize!); } } } diff --git a/lib/src/otf/table/post.dart b/lib/src/otf/table/post.dart index a4a4d48..a5159af 100644 --- a/lib/src/otf/table/post.dart +++ b/lib/src/otf/table/post.dart @@ -89,7 +89,7 @@ class PostScriptTableHeader implements BinaryCodable { abstract class PostScriptData implements BinaryCodable { PostScriptData(); - factory PostScriptData.fromByteData( + static PostScriptData? fromByteData( ByteData byteData, int offset, PostScriptTableHeader header) { final version = header.version.int32value; @@ -217,7 +217,7 @@ class PostScriptVersion20 extends PostScriptData { } class PostScriptTable extends FontTable { - PostScriptTable(TableRecordEntry entry, this.header, this.data) + PostScriptTable(TableRecordEntry? entry, this.header, this.data) : super.fromTableRecordEntry(entry); factory PostScriptTable.fromByteData( @@ -247,15 +247,15 @@ class PostScriptTable extends FontTable { } final PostScriptTableHeader header; - final PostScriptData data; + final PostScriptData? data; @override - int get size => header.size + data.size; + int get size => header.size + (data?.size ?? 0); @override void encodeToBinary(ByteData byteData) { header.encodeToBinary(byteData); - data.encodeToBinary(byteData.sublistView(header.size, data.size)); + data?.encodeToBinary(byteData.sublistView(header.size, data!.size)); } } diff --git a/lib/src/otf/table/script_list.dart b/lib/src/otf/table/script_list.dart index e63b79c..519ff4e 100644 --- a/lib/src/otf/table/script_list.dart +++ b/lib/src/otf/table/script_list.dart @@ -34,7 +34,7 @@ class ScriptRecord implements BinaryCodable { } final String scriptTag; - int scriptOffset; + int? scriptOffset; @override int get size => kScriptRecordSize; @@ -43,7 +43,7 @@ class ScriptRecord implements BinaryCodable { void encodeToBinary(ByteData byteData) { byteData ..setTag(0, scriptTag) - ..setUint16(4, scriptOffset); + ..setUint16(4, scriptOffset!); } } @@ -58,10 +58,10 @@ class ScriptTable implements BinaryCodable { factory ScriptTable.fromByteData( ByteData byteData, int offset, ScriptRecord record) { - offset += record.scriptOffset; + offset += record.scriptOffset!; final defaultLangSysOffset = byteData.getUint16(offset); - LanguageSystemTable defaultLangSys; + LanguageSystemTable? defaultLangSys; if (defaultLangSysOffset != 0) { defaultLangSys = LanguageSystemTable.fromByteData( byteData, offset + defaultLangSysOffset); @@ -91,7 +91,7 @@ class ScriptTable implements BinaryCodable { final List langSysRecords; final List langSysTables; - final LanguageSystemTable defaultLangSys; + final LanguageSystemTable? defaultLangSys; @override int get size { @@ -125,8 +125,8 @@ class ScriptTable implements BinaryCodable { final defaultRelativeLangSysOffset = tableRelativeOffset; byteData.setUint16(0, defaultRelativeLangSysOffset); - defaultLangSys.encodeToBinary(byteData.sublistView( - defaultRelativeLangSysOffset, defaultLangSys.size)); + defaultLangSys?.encodeToBinary(byteData.sublistView( + defaultRelativeLangSysOffset, defaultLangSys!.size)); } } diff --git a/lib/src/svg/element.dart b/lib/src/svg/element.dart index ca974f8..13f676c 100644 --- a/lib/src/svg/element.dart +++ b/lib/src/svg/element.dart @@ -4,13 +4,14 @@ import 'package:xml/xml.dart'; import '../utils/svg.dart'; import 'path.dart'; import 'shapes.dart'; +import 'unknown_element.dart'; abstract class SvgElement { - SvgElement(this.parent, this.xmlElement, {Matrix3 transform}) + SvgElement(this.parent, this.xmlElement, {Matrix3? transform}) : transform = transform ?? xmlElement?.parseTransformMatrix(); factory SvgElement.fromXmlElement( - SvgElement parent, XmlElement element, bool ignoreShapes) { + SvgElement? parent, XmlElement element, bool ignoreShapes) { switch (element.name.local) { case 'path': return PathElement.fromXmlElement(parent, element); @@ -28,25 +29,25 @@ abstract class SvgElement { return LineElement.fromXmlElement(parent, element); } - return null; + return UnknownElement(parent, element); } - final XmlElement xmlElement; - Matrix3 transform; - SvgElement parent; - - bool get hasTransform => transform != null && !transform.isIdentity(); + final XmlElement? xmlElement; + Matrix3? transform; + SvgElement? parent; /// Traverses parent elements and calculates result transform matrix. /// /// Returns result transform matrix or null, if there are no transforms. - Matrix3 getResultTransformMatrix() { + Matrix3? getResultTransformMatrix() { final transform = Matrix3.identity(); - var element = this; + SvgElement? element = this; while (element != null) { - if (element.hasTransform) { - transform.multiply(element.transform); + final elementTransform = element.transform; + + if (elementTransform != null) { + transform.multiply(elementTransform); } element = element.parent; @@ -57,11 +58,11 @@ abstract class SvgElement { } class GroupElement extends SvgElement { - GroupElement(this.elementList, SvgElement parent, XmlElement element) + GroupElement(this.elementList, SvgElement? parent, XmlElement element) : super(parent, element); factory GroupElement.fromXmlElement( - SvgElement parent, + SvgElement? parent, XmlElement element, bool ignoreShapes, ) { @@ -88,7 +89,7 @@ class GroupElement extends SvgElement { for (final c in elementList) { c.transform ??= Matrix3.identity(); - c.transform.multiply(transform); + c.transform!.multiply(transform!); } transform = null; diff --git a/lib/src/svg/path.dart b/lib/src/svg/path.dart index 7f4fc41..546decc 100644 --- a/lib/src/svg/path.dart +++ b/lib/src/svg/path.dart @@ -5,11 +5,11 @@ import '../utils/exception.dart'; import 'element.dart'; class PathElement extends SvgElement { - PathElement(this.fillRule, this.data, SvgElement parent, XmlElement element, - {Matrix3 transform}) + PathElement(this.fillRule, this.data, SvgElement? parent, XmlElement? element, + {Matrix3? transform}) : super(parent, element, transform: transform); - factory PathElement.fromXmlElement(SvgElement parent, XmlElement element) { + factory PathElement.fromXmlElement(SvgElement? parent, XmlElement element) { final dAttr = element.getAttribute('d'); if (dAttr == null) { @@ -21,6 +21,6 @@ class PathElement extends SvgElement { return PathElement(fillRule, dAttr, parent, element); } - final String fillRule; + final String? fillRule; final String data; } diff --git a/lib/src/svg/shapes.dart b/lib/src/svg/shapes.dart index 0d83ca5..fdf0de2 100644 --- a/lib/src/svg/shapes.dart +++ b/lib/src/svg/shapes.dart @@ -13,15 +13,15 @@ abstract class PathConvertible { class RectElement extends SvgElement implements PathConvertible { RectElement( - this.rectangle, this.rx, this.ry, SvgElement parent, XmlElement element) + this.rectangle, this.rx, this.ry, SvgElement? parent, XmlElement element) : super(parent, element); - factory RectElement.fromXmlElement(SvgElement parent, XmlElement element) { + factory RectElement.fromXmlElement(SvgElement? parent, XmlElement element) { final rect = math.Rectangle( - element.getScalarAttribute('x'), - element.getScalarAttribute('y'), - element.getScalarAttribute('width'), - element.getScalarAttribute('height'), + element.getScalarAttribute('x')!, + element.getScalarAttribute('y')!, + element.getScalarAttribute('width')!, + element.getScalarAttribute('height')!, ); var rx = element.getScalarAttribute('rx', zeroIfAbsent: false); @@ -61,14 +61,14 @@ class RectElement extends SvgElement implements PathConvertible { } class CircleElement extends SvgElement implements PathConvertible { - CircleElement(this.center, this.r, SvgElement parent, XmlElement element) + CircleElement(this.center, this.r, SvgElement? parent, XmlElement element) : super(parent, element); - factory CircleElement.fromXmlElement(SvgElement parent, XmlElement element) { + factory CircleElement.fromXmlElement(SvgElement? parent, XmlElement element) { final center = math.Point( - element.getScalarAttribute('cx'), element.getScalarAttribute('cy')); + element.getScalarAttribute('cx')!, element.getScalarAttribute('cy')!); - final r = element.getScalarAttribute('r'); + final r = element.getScalarAttribute('r')!; return CircleElement(center, r, parent, element); } @@ -90,12 +90,12 @@ class CircleElement extends SvgElement implements PathConvertible { } class PolylineElement extends SvgElement implements PathConvertible { - PolylineElement(this.points, SvgElement parent, XmlElement element) + PolylineElement(this.points, SvgElement? parent, XmlElement element) : super(parent, element); factory PolylineElement.fromXmlElement( - SvgElement parent, XmlElement element) { - final points = element.getAttribute('points'); + SvgElement? parent, XmlElement element) { + final points = element.getAttribute('points')!; return PolylineElement(points, parent, element); } @@ -111,11 +111,12 @@ class PolylineElement extends SvgElement implements PathConvertible { } class PolygonElement extends SvgElement implements PathConvertible { - PolygonElement(this.points, SvgElement parent, XmlElement element) + PolygonElement(this.points, SvgElement? parent, XmlElement element) : super(parent, element); - factory PolygonElement.fromXmlElement(SvgElement parent, XmlElement element) { - final points = element.getAttribute('points'); + factory PolygonElement.fromXmlElement( + SvgElement? parent, XmlElement element) { + final points = element.getAttribute('points')!; return PolygonElement(points, parent, element); } @@ -131,15 +132,15 @@ class PolygonElement extends SvgElement implements PathConvertible { } class LineElement extends SvgElement implements PathConvertible { - LineElement(this.p1, this.p2, SvgElement parent, XmlElement element) + LineElement(this.p1, this.p2, SvgElement? parent, XmlElement element) : super(parent, element); - factory LineElement.fromXmlElement(SvgElement parent, XmlElement element) { + factory LineElement.fromXmlElement(SvgElement? parent, XmlElement element) { final p1 = math.Point( - element.getScalarAttribute('x1'), element.getScalarAttribute('y1')); + element.getScalarAttribute('x1')!, element.getScalarAttribute('y1')!); final p2 = math.Point( - element.getScalarAttribute('x2'), element.getScalarAttribute('y2')); + element.getScalarAttribute('x2')!, element.getScalarAttribute('y2')!); return LineElement(p1, p2, parent, element); } diff --git a/lib/src/svg/svg.dart b/lib/src/svg/svg.dart index 98ee2e8..e4942d3 100644 --- a/lib/src/svg/svg.dart +++ b/lib/src/svg/svg.dart @@ -24,7 +24,7 @@ class Svg extends SvgElement { /// /// Throws [XmlParserException] if XML parsing exception occurs. /// Throws [SvgParserException] on any problem related to SVG parsing. - factory Svg.parse(String name, String xmlString, {bool ignoreShapes}) { + factory Svg.parse(String name, String xmlString, {bool? ignoreShapes}) { ignoreShapes ??= true; final xml = XmlDocument.parse(xmlString); @@ -34,12 +34,12 @@ class Svg extends SvgElement { throw SvgParserException('Root element must be SVG'); } - final vb = root + final parsedVb = root .getAttribute('viewBox') - .split(RegExp(r'[\s|,]')) - .where((e) => e != null) - .map(num.parse) - .toList(); + ?.split(RegExp(r'[\s|,]')) + .where((e) => e.isNotEmpty) + .map(num.parse); + final vb = [...?parsedVb]; if (vb.isEmpty || vb.length > 4) { throw SvgParserException('viewBox must contain 1..4 parameters'); diff --git a/lib/src/svg/transform.dart b/lib/src/svg/transform.dart index cb36ef1..b1e30ce 100644 --- a/lib/src/svg/transform.dart +++ b/lib/src/svg/transform.dart @@ -24,23 +24,23 @@ final _transformParameterRegExp = RegExp(r'[\w.-]+'); class Transform { Transform(this.type, this.parameterList); - final TransformType type; + final TransformType? type; final List parameterList; - static List parse(String string) { + static List parse(String? string) { if (string == null) { return []; } final transforms = _transformRegExp.allMatches(string).map((m) { - final name = m.group(1); + final name = m.group(1)!; final type = _kTransformNameMap.getKeyForValue(name); - final parameterString = m.group(2); + final parameterString = m.group(2)!; final parameterMatches = _transformParameterRegExp.allMatches(parameterString); final parameterList = - parameterMatches.map((m) => double.parse(m.group(0))).toList(); + parameterMatches.map((m) => double.parse(m.group(0)!)).toList(); return Transform(type, parameterList); }).toList(); @@ -48,7 +48,7 @@ class Transform { return transforms; } - Matrix3 get matrix { + Matrix3? get matrix { switch (type) { case TransformType.matrix: return Matrix3.fromList( @@ -83,16 +83,16 @@ class Transform { return _skewX(parameterList[0]); case TransformType.skewY: return _skewY(parameterList[0]); + case null: + return null; } - - return null; } } /// Generates transform matrix for a list of transforms. /// /// Returns null, if transformList is empty. -Matrix3 generateTransformMatrix(List transformList) { +Matrix3? generateTransformMatrix(List transformList) { if (transformList.isEmpty) { return null; } @@ -100,7 +100,9 @@ Matrix3 generateTransformMatrix(List transformList) { final matrix = Matrix3.identity(); for (final t in transformList) { - matrix.multiply(t.matrix); + if (t.matrix != null) { + matrix.multiply(t.matrix!); + } } return matrix; diff --git a/lib/src/svg/unknown_element.dart b/lib/src/svg/unknown_element.dart new file mode 100644 index 0000000..6b7ce33 --- /dev/null +++ b/lib/src/svg/unknown_element.dart @@ -0,0 +1,8 @@ +import 'package:xml/xml.dart'; + +import 'element.dart'; + +class UnknownElement extends SvgElement { + UnknownElement(SvgElement? parent, XmlElement? element) + : super(parent, element); +} diff --git a/lib/src/utils/enum_class.dart b/lib/src/utils/enum_class.dart index 336c608..23ecc7c 100644 --- a/lib/src/utils/enum_class.dart +++ b/lib/src/utils/enum_class.dart @@ -1,16 +1,18 @@ +import 'package:collection/collection.dart'; + class EnumClass { const EnumClass(this._map); final Map _map; - K getKeyForValue(V value) { - final entry = _map.entries - .firstWhere((entry) => entry.value == value, orElse: () => null); + K? getKeyForValue(V value) { + final entry = + _map.entries.firstWhereOrNull((entry) => entry.value == value); return entry?.key; } - V getValueForKey(K key) { + V? getValueForKey(K key) { return _map[key]; } @@ -22,5 +24,5 @@ class EnumClass { Map get map => _map; - V operator [](K key) => _map[key]; + V? operator [](K key) => _map[key]; } diff --git a/lib/src/utils/exception.dart b/lib/src/utils/exception.dart index 8f90303..fa0cea9 100644 --- a/lib/src/utils/exception.dart +++ b/lib/src/utils/exception.dart @@ -21,8 +21,8 @@ class ChecksumException implements Exception { class SvgParserException implements Exception { SvgParserException([this.message]); - final String message; + final String? message; @override - String toString() => message; + String toString() => 'SvgParserException($message)'; } diff --git a/lib/src/utils/flutter_class_gen.dart b/lib/src/utils/flutter_class_gen.dart index eba44fc..9527e81 100644 --- a/lib/src/utils/flutter_class_gen.dart +++ b/lib/src/utils/flutter_class_gen.dart @@ -28,11 +28,11 @@ class FlutterClassGenerator { /// * [indent] is a number of spaces in leading indentation for class' members. Defaults to 2. FlutterClassGenerator( this.glyphList, { - String className, - String familyName, - String fontFileName, - String package, - int indent, + String? className, + String? familyName, + String? fontFileName, + String? package, + int? indent, }) : _indent = ' ' * (indent ?? _kDefaultIndent), _className = _getVarName(className ?? _kDefaultClassName), _familyName = familyName ?? kDefaultFontFamily, @@ -45,7 +45,7 @@ class FlutterClassGenerator { final String _className; final String _familyName; final String _indent; - final String _package; + final String? _package; final List _iconVarNames; static List _generateVariableNames(List glyphList) { @@ -53,7 +53,7 @@ class FlutterClassGenerator { return glyphList.map((g) { final baseName = - _getVarName(p.basenameWithoutExtension(g.metadata.name)).snakeCase; + _getVarName(p.basenameWithoutExtension(g.metadata.name!)).snakeCase; final usingDefaultName = baseName.isEmpty; var variableName = usingDefaultName ? _kUnnamedIconName : baseName; @@ -67,8 +67,8 @@ class FlutterClassGenerator { var variableWithoutCount = variableName; if (countMatch != null) { - variableNameCount = int.parse(countMatch.group(2)); - variableWithoutCount = countMatch.group(1); + variableNameCount = int.parse(countMatch.group(2)!); + variableWithoutCount = countMatch.group(1)!; } String variableNameWithCount; @@ -97,8 +97,8 @@ class FlutterClassGenerator { List _generateIconConst(int index) { final glyphMeta = glyphList[index].metadata; - final charCode = glyphMeta.charCode; - final iconName = glyphMeta.name; + final charCode = glyphMeta.charCode!; + final iconName = glyphMeta.name!; final varName = _iconVarNames[index]; final hexCode = charCode.toRadixString(16); diff --git a/lib/src/utils/misc.dart b/lib/src/utils/misc.dart index a2793da..181b766 100644 --- a/lib/src/utils/misc.dart +++ b/lib/src/utils/misc.dart @@ -30,7 +30,7 @@ List> quadCurveToCubic( } extension MockableDateTime on DateTime { - static DateTime mockedDate; + static DateTime? mockedDate; static DateTime now() => mockedDate ?? DateTime.now(); } diff --git a/lib/src/utils/otf.dart b/lib/src/utils/otf.dart index bbe207c..c5339ef 100644 --- a/lib/src/utils/otf.dart +++ b/lib/src/utils/otf.dart @@ -151,7 +151,7 @@ extension OTFByteDateExt on ByteData { setInt64(offset, dateTime.difference(_longDateTimeStart).inSeconds); } - ByteData sublistView(int offset, [int length]) { + ByteData sublistView(int offset, [int? length]) { return ByteData.sublistView( this, offset, length == null ? null : offset + length); } @@ -169,7 +169,7 @@ extension OTFStringExt on String { @immutable class Revision { - const Revision(int major, int minor) + const Revision(int? major, int? minor) : major = major ?? 0, minor = minor ?? 0; diff --git a/lib/src/utils/svg.dart b/lib/src/utils/svg.dart index 9a632f6..e5b0406 100644 --- a/lib/src/utils/svg.dart +++ b/lib/src/utils/svg.dart @@ -4,10 +4,11 @@ import 'package:xml/xml.dart'; import '../svg/element.dart'; import '../svg/shapes.dart'; import '../svg/transform.dart'; +import '../svg/unknown_element.dart'; extension XmlElementExt on XmlElement { - num getScalarAttribute(String name, - {String namespace, bool zeroIfAbsent = true}) { + num? getScalarAttribute(String name, + {String? namespace, bool zeroIfAbsent = true}) { final attr = getAttribute(name, namespace: namespace); if (attr == null) { @@ -17,21 +18,20 @@ extension XmlElementExt on XmlElement { return num.parse(attr); } - List parseSvgElements(SvgElement parent, bool ignoreShapes) { + List parseSvgElements(SvgElement? parent, bool ignoreShapes) { var elements = children .whereType() .map((e) => SvgElement.fromXmlElement(parent, e, ignoreShapes)) // Ignoring unknown elements - .where((e) => e != null) + .where((e) => e is! UnknownElement) // Expanding groups .expand((e) { if (e is! GroupElement) { return [e]; } - final g = e as GroupElement..applyTransformOnChildren(); - - return g.elementList; + e.applyTransformOnChildren(); + return e.elementList; }); if (!ignoreShapes) { @@ -43,7 +43,7 @@ extension XmlElementExt on XmlElement { return elements.toList(); } - Matrix3 parseTransformMatrix() { + Matrix3? parseTransformMatrix() { final transformList = Transform.parse(getAttribute('transform')); return generateTransformMatrix(transformList); } diff --git a/pubspec.yaml b/pubspec.yaml index 61fa050..b64ecc5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,27 +1,28 @@ name: fontify -version: 0.2.0 +version: 0.3.0-nullsafety.0 description: >- Converts SVG icons to OTF font and generates Flutter-compatible class. Provides an API and a CLI tool. homepage: https://github.com/westracer/fontify environment: - sdk: ">=2.6.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: - meta: ^1.1.8 - xml: ^4.2.0 - path: ^1.7.0 - path_parsing: ^0.1.4 - vector_math: ^2.0.8 - logger: ^0.9.1 - recase: ^3.0.0 - args: ^1.6.0 - yaml: ^2.2.1 + args: ^2.0.0 + collection: ^1.15.0 + logger: ^1.0.0 + meta: ^1.3.0 + path: ^1.8.0 + path_parsing: ^0.2.0 + recase: ^4.0.0 + vector_math: ^2.1.0 + xml: ^5.1.0 + yaml: ^3.1.0 dev_dependencies: - pedantic: ^1.9.0 - test: ^1.14.7 + pedantic: ^1.11.0 + test: ^1.17.0 executables: fontify: fontify \ No newline at end of file diff --git a/test/assets/test_config.yaml b/test/assets/test_config.yaml index e51c42b..ccd015c 100644 --- a/test/assets/test_config.yaml +++ b/test/assets/test_config.yaml @@ -15,4 +15,8 @@ fontify: ignore_shapes: false recursive: false - verbose: false \ No newline at end of file + verbose: false + + # Unknown config parameters + testing: true + 1: 1 \ No newline at end of file diff --git a/test/cli_test.dart b/test/cli_test.dart index 74b4826..d47039d 100644 --- a/test/cli_test.dart +++ b/test/cli_test.dart @@ -9,7 +9,7 @@ void main() { defineOptions(_argParser); void expectCliArgumentException(List args) { - expect(() => parseArguments(_argParser, args).validate(), + expect(() => parseArguments(_argParser, args), throwsA(const TypeMatcher())); } @@ -51,11 +51,11 @@ void main() { '--package=test_package', ]; - final parsedArgs = parseArguments(_argParser, args)..validate(); + final parsedArgs = parseArguments(_argParser, args); expect(parsedArgs.svgDir.path, args.first); expect(parsedArgs.fontFile.path, args[1]); - expect(parsedArgs.classFile.path, 'test/a/df.dart'); + expect(parsedArgs.classFile?.path, 'test/a/df.dart'); expect(parsedArgs.indent, 4); expect(parsedArgs.className, 'MyIcons'); expect(parsedArgs.fontName, 'My Icons'); @@ -63,7 +63,7 @@ void main() { expect(parsedArgs.ignoreShapes, isFalse); expect(parsedArgs.recursive, isTrue); expect(parsedArgs.verbose, isTrue); - expect(parsedArgs.configFile.path, 'test/config.yaml'); + expect(parsedArgs.configFile?.path, 'test/config.yaml'); expect(parsedArgs.fontPackage, 'test_package'); }); @@ -75,7 +75,7 @@ void main() { '--ignore-shapes', ]; - final parsedArgs = parseArguments(_argParser, args)..validate(); + final parsedArgs = parseArguments(_argParser, args); expect(parsedArgs.svgDir.path, args.first); expect(parsedArgs.fontFile.path, args[1]); @@ -93,7 +93,7 @@ void main() { test('Help', () { void expectCliHelpException(List args) { - expect(() => parseArguments(_argParser, args).validate(), + expect(() => parseArguments(_argParser, args), throwsA(const TypeMatcher())); } @@ -120,7 +120,7 @@ void main() { test('All arguments and config', () { const args = [ - 'no', + './', 'no', '--output-class-file=no', '--indent=0', @@ -134,11 +134,11 @@ void main() { '--config-file=test/assets/test_config.yaml', ]; - final parsedArgs = parseArgsAndConfig(_argParser, args)..validate(); + final parsedArgs = parseArgsAndConfig(_argParser, args); expect(parsedArgs.svgDir.path, './'); expect(parsedArgs.fontFile.path, 'generated_font.otf'); - expect(parsedArgs.classFile.path, 'lib/test_font.dart'); + expect(parsedArgs.classFile?.path, 'lib/test_font.dart'); expect(parsedArgs.indent, 4); expect(parsedArgs.className, 'MyIcons'); expect(parsedArgs.fontName, 'My Icons'); @@ -152,14 +152,16 @@ void main() { test('No arguments and config', () { const args = [ + './', + 'yes', '--config-file=test/assets/test_config.yaml', ]; - final parsedArgs = parseArgsAndConfig(_argParser, args)..validate(); + final parsedArgs = parseArgsAndConfig(_argParser, args); expect(parsedArgs.svgDir.path, './'); expect(parsedArgs.fontFile.path, 'generated_font.otf'); - expect(parsedArgs.classFile.path, 'lib/test_font.dart'); + expect(parsedArgs.classFile?.path, 'lib/test_font.dart'); expect(parsedArgs.indent, 4); expect(parsedArgs.className, 'MyIcons'); expect(parsedArgs.fontName, 'My Icons'); @@ -174,7 +176,7 @@ void main() { group('Config', () { void expectCliArgumentException(String cfg) { - expect(() => parseConfig(cfg).validate(), + expect(() => parseConfig(cfg), throwsA(const TypeMatcher())); } @@ -243,19 +245,19 @@ fontify: recursive: true verbose: true - ''')..validate(); + '''); - expect(parsedArgs.svgDir.path, './'); - expect(parsedArgs.fontFile.path, 'generated_font.otf'); - expect(parsedArgs.classFile.path, 'lib/test_font.dart'); - expect(parsedArgs.indent, 4); - expect(parsedArgs.className, 'MyIcons'); - expect(parsedArgs.fontName, 'My Icons'); - expect(parsedArgs.normalize, isFalse); - expect(parsedArgs.ignoreShapes, isFalse); - expect(parsedArgs.recursive, isTrue); - expect(parsedArgs.verbose, isTrue); - expect(parsedArgs.fontPackage, 'test_package'); + expect(parsedArgs?.svgDir.path, './'); + expect(parsedArgs?.fontFile.path, 'generated_font.otf'); + expect(parsedArgs?.classFile?.path, 'lib/test_font.dart'); + expect(parsedArgs?.indent, 4); + expect(parsedArgs?.className, 'MyIcons'); + expect(parsedArgs?.fontName, 'My Icons'); + expect(parsedArgs?.normalize, isFalse); + expect(parsedArgs?.ignoreShapes, isFalse); + expect(parsedArgs?.recursive, isTrue); + expect(parsedArgs?.verbose, isTrue); + expect(parsedArgs?.fontPackage, 'test_package'); }); test('All arguments with defaults', () { @@ -263,19 +265,19 @@ fontify: fontify: input_svg_dir: ./ output_font_file: generated_font.otf - ''')..validate(); + '''); - expect(parsedArgs.svgDir.path, './'); - expect(parsedArgs.fontFile.path, 'generated_font.otf'); - expect(parsedArgs.classFile, isNull); - expect(parsedArgs.indent, null); - expect(parsedArgs.className, isNull); - expect(parsedArgs.fontName, isNull); - expect(parsedArgs.normalize, isNull); - expect(parsedArgs.ignoreShapes, isNull); - expect(parsedArgs.recursive, isNull); - expect(parsedArgs.verbose, isNull); - expect(parsedArgs.fontPackage, isNull); + expect(parsedArgs?.svgDir.path, './'); + expect(parsedArgs?.fontFile.path, 'generated_font.otf'); + expect(parsedArgs?.classFile, isNull); + expect(parsedArgs?.indent, null); + expect(parsedArgs?.className, isNull); + expect(parsedArgs?.fontName, isNull); + expect(parsedArgs?.normalize, isNull); + expect(parsedArgs?.ignoreShapes, isNull); + expect(parsedArgs?.recursive, isNull); + expect(parsedArgs?.verbose, isNull); + expect(parsedArgs?.fontPackage, isNull); }); test('Type validation', () { diff --git a/test/ttf_test.dart b/test/ttf_test.dart index b19c219..de1b71a 100644 --- a/test/ttf_test.dart +++ b/test/ttf_test.dart @@ -18,7 +18,7 @@ const _kTestFontAssetPath = '$kTestAssetsDir/test_font.ttf'; const _kTestCFF2fontAssetPath = '$kTestAssetsDir/test_cff2_font.otf'; void main() { - OpenTypeFont font; + late OpenTypeFont font; group('Reader', () { setUpAll(() { @@ -285,19 +285,19 @@ void main() { expect(scriptTable.scriptRecords[0].scriptTag, 'DFLT'); expect(scriptTable.scriptTables[0].langSysCount, 0); - expect(scriptTable.scriptTables[0].defaultLangSys.featureIndexCount, 1); - expect(scriptTable.scriptTables[0].defaultLangSys.featureIndices, [0]); - expect(scriptTable.scriptTables[0].defaultLangSys.lookupOrder, 0); + expect(scriptTable.scriptTables[0].defaultLangSys?.featureIndexCount, 1); + expect(scriptTable.scriptTables[0].defaultLangSys?.featureIndices, [0]); + expect(scriptTable.scriptTables[0].defaultLangSys?.lookupOrder, 0); expect( - scriptTable.scriptTables[0].defaultLangSys.requiredFeatureIndex, 0); + scriptTable.scriptTables[0].defaultLangSys?.requiredFeatureIndex, 0); expect(scriptTable.scriptRecords[1].scriptTag, 'latn'); expect(scriptTable.scriptTables[1].langSysCount, 0); - expect(scriptTable.scriptTables[1].defaultLangSys.featureIndexCount, 1); - expect(scriptTable.scriptTables[1].defaultLangSys.featureIndices, [0]); - expect(scriptTable.scriptTables[1].defaultLangSys.lookupOrder, 0); + expect(scriptTable.scriptTables[1].defaultLangSys?.featureIndexCount, 1); + expect(scriptTable.scriptTables[1].defaultLangSys?.featureIndices, [0]); + expect(scriptTable.scriptTables[1].defaultLangSys?.lookupOrder, 0); expect( - scriptTable.scriptTables[1].defaultLangSys.requiredFeatureIndex, 0); + scriptTable.scriptTables[1].defaultLangSys?.requiredFeatureIndex, 0); final featureTable = table.featureListTable; expect(featureTable.featureCount, 1); @@ -328,8 +328,8 @@ void main() { }); group('Creation & Writer', () { - ByteData originalByteData, recreatedByteData; - OpenTypeFont recreatedFont; + late ByteData originalByteData, recreatedByteData; + late OpenTypeFont recreatedFont; setUpAll(() { MockableDateTime.mockedDate = DateTime.utc(2020, 2, 2, 2, 2); @@ -368,20 +368,20 @@ void main() { const expected = 'AAEAAAABAADXpqNjXw889QALBAAAAAAA2lveGAAAAADaW94Y//X/ZwZkBAAAAAAIAAIAAAAA'; final actual = base64Encode(recreatedByteData.buffer.asUint8List( - recreatedFont.head.entry.offset, recreatedFont.head.entry.length)); + recreatedFont.head.entry!.offset, recreatedFont.head.entry!.length)); expect(actual, expected); - expect(recreatedFont.head.entry.checkSum, 439353492); + expect(recreatedFont.head.entry!.checkSum, 439353492); }, skip: "Font's checksum is always changing, unskip later"); test('Glyph Substitution table', () { const expected = 'AAEAAAAKADAAPgACREZMVAAObGF0bgAaAAQAAAAA//8AAQAAAAQAAAAA//8AAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAA'; final actual = base64Encode(recreatedByteData.buffer.asUint8List( - recreatedFont.gsub.entry.offset, recreatedFont.gsub.entry.length)); + recreatedFont.gsub.entry!.offset, recreatedFont.gsub.entry!.length)); expect(actual, expected); - expect(recreatedFont.gsub.entry.checkSum, 546121080); + expect(recreatedFont.gsub.entry!.checkSum, 546121080); }); test('OS/2 V5', () { @@ -393,7 +393,7 @@ void main() { }); group('CFF', () { - ByteData byteData; + late ByteData byteData; setUpAll(() { byteData = @@ -405,10 +405,10 @@ void main() { final table = font.cff2; final originalCFF2byteList = - byteData.buffer.asUint8List(table.entry.offset, table.size).toList(); + byteData.buffer.asUint8List(table.entry!.offset, table.size).toList(); final encodedCFF2byteData = ByteData(table.size); - expect(table.size, table.entry.length); + expect(table.size, table.entry!.length); table ..recalculateOffsets()