diff --git a/packages/vector_graphics_compiler/lib/src/paint.dart b/packages/vector_graphics_compiler/lib/src/paint.dart index 321e945d..a46fc6c8 100644 --- a/packages/vector_graphics_compiler/lib/src/paint.dart +++ b/packages/vector_graphics_compiler/lib/src/paint.dart @@ -1328,6 +1328,7 @@ class TextConfig { this.decoration, this.decorationStyle, this.decorationColor, + this.letterSpacing, ); /// The text to be rendered. @@ -1354,6 +1355,9 @@ class TextConfig { /// The decoration style to apply to the text. final TextDecorationStyle decorationStyle; + /// The space between letters. + final double letterSpacing; + /// The color to use for the decoration, if any. final Color decorationColor; @@ -1367,6 +1371,7 @@ class TextConfig { decoration, decorationStyle, decorationColor, + letterSpacing, ); @override @@ -1379,7 +1384,8 @@ class TextConfig { other.fontWeight == fontWeight && other.decoration == decoration && other.decorationStyle == decorationStyle && - other.decorationColor == decorationColor; + other.decorationColor == decorationColor && + other.letterSpacing == letterSpacing; } @override @@ -1392,7 +1398,9 @@ class TextConfig { '$fontSize, ' '$decoration, ' '$decorationStyle, ' - '$decorationColor,)'; + '$decorationColor, ' + '$letterSpacing' + ')'; } } diff --git a/packages/vector_graphics_compiler/lib/src/svg/node.dart b/packages/vector_graphics_compiler/lib/src/svg/node.dart index fe6d960c..6eeb5d10 100644 --- a/packages/vector_graphics_compiler/lib/src/svg/node.dart +++ b/packages/vector_graphics_compiler/lib/src/svg/node.dart @@ -544,6 +544,7 @@ class TextNode extends AttributedNode { attributes.textDecoration ?? TextDecoration.none, attributes.textDecorationStyle ?? TextDecorationStyle.solid, attributes.textDecorationColor ?? Color.opaqueBlack, + attributes.letterSpacing ?? 0, ); } diff --git a/packages/vector_graphics_compiler/lib/src/svg/parser.dart b/packages/vector_graphics_compiler/lib/src/svg/parser.dart index 766d211c..ea04cfe9 100644 --- a/packages/vector_graphics_compiler/lib/src/svg/parser.dart +++ b/packages/vector_graphics_compiler/lib/src/svg/parser.dart @@ -167,24 +167,26 @@ class _Elements { final String id = parserState.buildUrlIri(); parserState.patternIds.add(id); final SvgAttributes newAttributes = SvgAttributes._( - raw: attributes.raw, - id: attributes.id, - href: attributes.href, - transform: attributes.transform, - color: attributes.color, - stroke: attributes.stroke, - fill: attributes.fill, - fillRule: attributes.fillRule, - clipRule: attributes.clipRule, - clipPathId: attributes.clipPathId, - blendMode: attributes.blendMode, - fontFamily: attributes.fontFamily, - fontWeight: attributes.fontWeight, - fontSize: attributes.fontSize, - x: DoubleOrPercentage.fromString(rawX), - y: DoubleOrPercentage.fromString(rawY), - width: patternWidth, - height: patternHeight); + raw: attributes.raw, + id: attributes.id, + href: attributes.href, + transform: attributes.transform, + color: attributes.color, + stroke: attributes.stroke, + fill: attributes.fill, + fillRule: attributes.fillRule, + clipRule: attributes.clipRule, + clipPathId: attributes.clipPathId, + blendMode: attributes.blendMode, + fontFamily: attributes.fontFamily, + fontWeight: attributes.fontWeight, + fontSize: attributes.fontSize, + x: DoubleOrPercentage.fromString(rawX), + y: DoubleOrPercentage.fromString(rawY), + width: patternWidth, + height: patternHeight, + letterSpacing: attributes.letterSpacing, + ); final ParentNode group = ParentNode(newAttributes); parserState.addGroup(parserState._currentStartElement!, group); @@ -1659,43 +1661,45 @@ class SvgParser { final String? rawDy = attributeMap['dy']; return SvgAttributes._( - raw: attributeMap, - id: id, - x: DoubleOrPercentage.fromString(rawX), - y: DoubleOrPercentage.fromString(rawY), - dx: DoubleOrPercentage.fromString(rawDx), - dy: DoubleOrPercentage.fromString(rawDy), - href: attributeMap['href'], - color: attributeMap['color']?.toLowerCase() == 'none' - ? const ColorOrNone.none() - : ColorOrNone.color(color), - stroke: _parseStrokeAttributes( - attributeMap, - opacity, - color, - id, - ), - fill: _parseFillAttributes( - attributeMap, - opacity, - color, - id, - ), - fillRule: parseRawFillRule(attributeMap['fill-rule']), - clipRule: parseRawFillRule(attributeMap['clip-rule']), - clipPathId: attributeMap['clip-path'], - blendMode: _blendModes[attributeMap['mix-blend-mode']], - transform: - parseTransform(attributeMap['transform']) ?? AffineMatrix.identity, - fontFamily: attributeMap['font-family'], - fontWeight: parseFontWeight(attributeMap['font-weight']), - fontSize: parseFontSize(attributeMap['font-size']), - textDecoration: parseTextDecoration(attributeMap['text-decoration']), - textDecorationStyle: - parseTextDecorationStyle(attributeMap['text-decoration-style']), - textDecorationColor: parseColor(attributeMap['text-decoration-color'], - attributeName: 'text-decoration-color', id: id), - textAnchorMultiplier: parseTextAnchor(attributeMap['text-anchor'])); + raw: attributeMap, + id: id, + x: DoubleOrPercentage.fromString(rawX), + y: DoubleOrPercentage.fromString(rawY), + dx: DoubleOrPercentage.fromString(rawDx), + dy: DoubleOrPercentage.fromString(rawDy), + href: attributeMap['href'], + color: attributeMap['color']?.toLowerCase() == 'none' + ? const ColorOrNone.none() + : ColorOrNone.color(color), + stroke: _parseStrokeAttributes( + attributeMap, + opacity, + color, + id, + ), + fill: _parseFillAttributes( + attributeMap, + opacity, + color, + id, + ), + fillRule: parseRawFillRule(attributeMap['fill-rule']), + clipRule: parseRawFillRule(attributeMap['clip-rule']), + clipPathId: attributeMap['clip-path'], + blendMode: _blendModes[attributeMap['mix-blend-mode']], + transform: + parseTransform(attributeMap['transform']) ?? AffineMatrix.identity, + fontFamily: attributeMap['font-family'], + fontWeight: parseFontWeight(attributeMap['font-weight']), + fontSize: parseFontSize(attributeMap['font-size']), + textDecoration: parseTextDecoration(attributeMap['text-decoration']), + textDecorationStyle: + parseTextDecorationStyle(attributeMap['text-decoration-style']), + textDecorationColor: parseColor(attributeMap['text-decoration-color'], + attributeName: 'text-decoration-color', id: id), + textAnchorMultiplier: parseTextAnchor(attributeMap['text-anchor']), + letterSpacing: parseDoubleWithUnits(attributeMap['letter-spacing']), + ); } } @@ -1872,6 +1876,7 @@ class SvgAttributes { this.dy, this.width, this.height, + this.letterSpacing, }); /// For use in tests to construct arbitrary attributes. @@ -1901,6 +1906,7 @@ class SvgAttributes { this.dy, this.width, this.height, + this.letterSpacing, }); /// The empty set of properties. @@ -2056,6 +2062,9 @@ class SvgAttributes { /// The relative y translation. final DoubleOrPercentage? dy; + /// The space between the letters. + final double? letterSpacing; + /// A copy of these attributes after absorbing a saveLayer. /// /// Specifically, this will null out `blendMode` and any opacity related @@ -2086,6 +2095,7 @@ class SvgAttributes { y: y, width: width, height: height, + letterSpacing: letterSpacing, ); } @@ -2131,6 +2141,7 @@ class SvgAttributes { y: y, dx: dx, dy: dy, + letterSpacing: letterSpacing ?? parent.letterSpacing, ); } } diff --git a/packages/vector_graphics_compiler/test/parser_test.dart b/packages/vector_graphics_compiler/test/parser_test.dart index e32f4a23..2c11fb90 100644 --- a/packages/vector_graphics_compiler/test/parser_test.dart +++ b/packages/vector_graphics_compiler/test/parser_test.dart @@ -1,6 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:vector_graphics_compiler/src/svg/numbers.dart'; import 'package:vector_graphics_compiler/vector_graphics_compiler.dart'; + import 'test_svg_strings.dart'; void main() { @@ -50,6 +51,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + 0, ), TextConfig( 'Tōkyū Railways route map', @@ -60,6 +62,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + 0, ), ]); expect(instructions.textPositions, [ @@ -134,6 +137,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + 0, ), TextConfig( '3', @@ -144,6 +148,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + 0, ), TextConfig( '1', @@ -154,6 +159,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + 0, ), ]); }); @@ -193,7 +199,7 @@ void main() { const String svg = ''' - + π( D² - d² )( N - N u ) @@ -219,6 +225,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + -0.5, ), TextConfig( ' u', @@ -229,6 +236,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + -0.5, ), TextConfig( ' )', @@ -239,6 +247,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + -0.5, ), ]); }); @@ -273,6 +282,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + 0.126, ), TextConfig( 'more text.', @@ -283,6 +293,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + 0.126, ), TextConfig( 'Even more text', @@ -293,6 +304,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + 0.126, ), TextConfig( 'text everywhere', @@ -303,6 +315,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + 0.126, ), TextConfig( 'so many lines', @@ -313,6 +326,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + 0.126, ), ], ); @@ -579,6 +593,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + 0, ), TextConfig( 'Text anchor middle', @@ -589,6 +604,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + 0, ), TextConfig( 'Text anchor end', @@ -599,6 +615,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + 0, ) ]); }); @@ -618,6 +635,7 @@ void main() { TextDecoration.overline, TextDecorationStyle.solid, Color(0xffff0000), + 0, ), TextConfig( 'Strike text', @@ -628,6 +646,7 @@ void main() { TextDecoration.lineThrough, TextDecorationStyle.solid, Color(0xff008000), + 0, ), TextConfig( 'Underline text', @@ -638,6 +657,7 @@ void main() { TextDecoration.underline, TextDecorationStyle.double, Color(0xff008000), + 0, ) ]); }); @@ -782,6 +802,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color.opaqueBlack, + 0, ), ); }); @@ -1404,6 +1425,7 @@ void main() { TextDecoration.none, TextDecorationStyle.solid, Color(0xff000000), + 0, ) ], );