Skip to content
This repository has been archived by the owner on Apr 29, 2021. It is now read-only.

Implement missing commands #7

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@ svgPathToSource('M0,15,L15,15L7.5,0z')
Currently supported
---

- Parsing a single SVG path that contains move, line, and close commands.
- Parsing a single SVG spec compatible path.
- Converting a single SVG path to raw Dart (generated) code.
7 changes: 5 additions & 2 deletions lib/src/command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ class SvgPathCurveQuadraticSegment extends SvgPathPositionSegment {
bool isRelative: false})
: super(x, y, SvgPathSegmentType.CurveTo, isRelative: isRelative);

/// Whether there is no control point (a smooth curve)
bool get isSmooth => x1 == null && y1 == null;

@override
bool operator==(o) {
if (o is! SvgPathCurveQuadraticSegment) return false;
Expand All @@ -117,7 +120,7 @@ class SvgPathCurveQuadraticSegment extends SvgPathPositionSegment {
}

@override
String toString() => 'SvgPathCurveCubicSegment ' + {
String toString() => 'SvgPathCurveQuadraticSegment ' + {
'x': x,
'y': y,
'x1': x1,
Expand Down Expand Up @@ -186,7 +189,7 @@ class SvgPathArcSegment extends SvgPathPositionSegment {
/// The value of the sweep-flag parameter.
final bool isSweep;

SvgPathArcSegment(
const SvgPathArcSegment(
num x,
num y,
this.r1,
Expand Down
302 changes: 237 additions & 65 deletions lib/src/grammar.dart
Original file line number Diff line number Diff line change
@@ -1,125 +1,297 @@
library svg.src.grammar;

import 'package:petitparser/petitparser.dart';
import 'package:petitparser/petitparser.dart' as pp;

/// An implementation of the the [WC3 SVG Path Grammar]
/// (http://www.w3.org/TR/SVG/paths.html) for Dart.
class SvgGrammarDefinition extends GrammarDefinition {
///
/// The grammar follows the BNF grammar provided in the
/// [SVG specifications](https://www.w3.org/TR/SVG/paths.html#PathDataBNF).
class SvgGrammarDefinition extends pp.GrammarDefinition {
const SvgGrammarDefinition();

@override
start() => svgPath();

/// wsp* moveto-drawto-command-groups? wsp*
/// svg-path:
/// wsp* moveto-drawto-command-groups? wsp*
svgPath() =>
whitespace().star() &
wsp().star() &
moveToDrawToCommandGroups().optional() &
whitespace().star();
wsp().star();

/// moveto-drawto-command-group
/// | moveto-drawto-command-group wsp* moveto-drawto-command-groups
/// moveto-drawto-command-groups:
/// moveto-drawto-command-group
/// | moveto-drawto-command-group wsp* moveto-drawto-command-groups
moveToDrawToCommandGroups() =>
(moveToDrawToCommandGroup() &
whitespace().star() &
wsp().star() &
ref(moveToDrawToCommandGroups))
| moveToDrawToCommandGroup();

/// moveto wsp* drawto-commands?
/// moveto-drawto-command-group:
/// moveto wsp* drawto-commands?
moveToDrawToCommandGroup() =>
moveTo() & commaWhitespace().optional() & drawToCommands();
moveTo() & commaWsp().optional() & drawToCommands();

/// drawto-command
/// | drawto-command wsp* drawto-commands
/// drawto-commands:
/// drawto-command
/// | drawto-command wsp* drawto-commands
drawToCommands() =>
(drawToCommand() & whitespace().star() & ref(drawToCommands))
(drawToCommand() & wsp().star() & ref(drawToCommands))
| drawToCommand();

/// closepath
/// | lineto
/// | horizontal-lineto
/// | vertical-lineto
/// | curveto
/// | smooth-curveto
/// | quadratic-bezier-curveto
/// | smooth-quadratic-bezier-curveto
/// | elliptical-arc
drawToCommand() => closePath() | lineTo();

/// ( "M" | "m" ) wsp* moveto-argument-sequence
moveTo() => pattern('Mm') & whitespace().star() & moveToArgumentSequence();

/// coordinate-pair
/// | coordinate-pair comma-wsp? lineto-argument-sequence
/// drawto-command:
/// closepath
/// | lineto
/// | horizontal-lineto
/// | vertical-lineto
/// | curveto
/// | smooth-curveto
/// | quadratic-bezier-curveto
/// | smooth-quadratic-bezier-curveto
/// | elliptical-arc
drawToCommand() =>
closePath() | lineTo() | horizontalLineTo()
| verticalLineTo() | curveTo() | smoothCurveTo()
| quadraticBezierCurveTo() | smoothQuadraticBezierCurveTo()
| ellipticalArc();

/// moveto:
/// ( "M" | "m" ) wsp* moveto-argument-sequence
moveTo() => pp.pattern('Mm') & wsp().star() & moveToArgumentSequence();

/// moveto-argument-sequence:
/// coordinate-pair
/// | coordinate-pair comma-wsp? lineto-argument-sequence
moveToArgumentSequence() =>
(coordinatePair() &
commaWhitespace().optional() &
commaWsp().optional() &
lineToArgumentSequence())
| coordinatePair();

/// ( "Z" | "z" )
closePath() => pattern('Zz');
/// closepath:
/// ("Z" | "z")
closePath() => pp.pattern('Zz');

/// ( "L" | "l" ) wsp* lineto-argument-sequence
lineTo() => pattern('Ll') & whitespace().star() & lineToArgumentSequence();
/// lineto:
/// ( "L" | "l" ) wsp* lineto-argument-sequence
lineTo() => pp.pattern('Ll') & wsp().star() & lineToArgumentSequence();

/// coordinate-pair
/// | coordinate-pair comma-wsp? lineto-argument-sequence
/// lineto-argument-sequence:
/// coordinate-pair
/// | coordinate-pair comma-wsp? lineto-argument-sequence
lineToArgumentSequence() =>
(coordinatePair() &
commaWhitespace().optional() &
commaWsp().optional() &
ref(lineToArgumentSequence))
| coordinatePair();

/// coordinate comma-wsp? coordinate
/// horizontal-lineto:
/// ( "H" | "h" ) wsp* horizontal-lineto-argument-sequence
horizontalLineTo() =>
pp.pattern('Hh') & wsp().star() & horizontalLineToArgumentSequence();

/// horizontal-lineto-argument-sequence:
/// coordinate
/// | coordinate comma-wsp? horizontal-lineto-argument-sequence
horizontalLineToArgumentSequence() =>
(coordinate() &
commaWsp().optional() &
ref(horizontalLineToArgumentSequence))
| coordinate();

/// vertical-lineto:
/// ( "V" | "v" ) wsp* vertical-lineto-argument-sequence
verticalLineTo() =>
pp.pattern('Vv') & wsp().star() & verticalLineToArgumentSequence();

/// vertical-lineto-argument-sequence:
/// coordinate
/// | coordinate comma-wsp? vertical-lineto-argument-sequence
verticalLineToArgumentSequence() =>
(coordinate() &
commaWsp().optional() &
ref(verticalLineToArgumentSequence))
| coordinate();

/// curveto:
/// ( "C" | "c" ) wsp* curveto-argument-sequence
curveTo() =>
pp.pattern('Cc') & wsp().star() & curveToArgumentSequence();

/// curveto-argument-sequence:
/// curveto-argument
/// | curveto-argument comma-wsp? curveto-argument-sequence
curveToArgumentSequence() =>
(curveToArgument() &
commaWsp().optional() &
ref(curveToArgumentSequence))
| curveToArgument();

/// curveto-argument:
/// coordinate-pair comma-wsp? coordinate-pair comma-wsp? coordinate-pair
curveToArgument() =>
coordinatePair() & commaWsp().optional() &
coordinatePair() & commaWsp().optional() &
coordinatePair();

/// smooth-curveto:
/// ( "S" | "s" ) wsp* smooth-curveto-argument-sequence
smoothCurveTo() =>
pp.pattern('Ss') & wsp().star() & smoothCurveToArgumentSequence();

/// smooth-curveto-argument-sequence:
/// smooth-curveto-argument
/// | smooth-curveto-argument comma-wsp? smooth-curveto-argument-sequence
smoothCurveToArgumentSequence() =>
(smoothCurvetoArgument() &
commaWsp().optional() &
ref(smoothCurveToArgumentSequence))
| smoothCurvetoArgument();

/// smooth-curveto-argument:
/// coordinate-pair comma-wsp? coordinate-pair
smoothCurvetoArgument() =>
coordinatePair() & commaWsp().optional() &
coordinatePair();

/// quadratic-bezier-curveto:
/// ( "Q" | "q" ) wsp* quadratic-bezier-curveto-argument-sequence
quadraticBezierCurveTo() =>
pp.pattern('Qq') & wsp().star() &
quadraticBezierCurveToArgumentSequence();

/// quadratic-bezier-curveto-argument-sequence:
/// quadratic-bezier-curveto-argument
/// | quadratic-bezier-curveto-argument comma-wsp?
/// quadratic-bezier-curveto-argument-sequence
quadraticBezierCurveToArgumentSequence() =>
(quadraticBezierCurveToArgument() &
commaWsp().optional() &
ref(quadraticBezierCurveToArgumentSequence))
| quadraticBezierCurveToArgument();

/// quadratic-bezier-curveto-argument:
/// coordinate-pair comma-wsp? coordinate-pair
quadraticBezierCurveToArgument() =>
coordinatePair() & commaWsp().optional() &
coordinatePair();

/// smooth-quadratic-bezier-curveto:
/// ( "T" | "t" ) wsp* smooth-quadratic-bezier-curveto-argument-sequence
smoothQuadraticBezierCurveTo() =>
pp.pattern('Tt') & wsp().star() &
smoothQuadraticBezierCurveToArgumentSequence();

/// smooth-quadratic-bezier-curveto-argument-sequence:
/// coordinate-pair
/// | coordinate-pair comma-wsp? smooth-quadratic-bezier-curveto-argument-sequence
smoothQuadraticBezierCurveToArgumentSequence() =>
(coordinatePair() &
commaWsp().optional() &
ref(smoothQuadraticBezierCurveToArgumentSequence))
| coordinatePair();

/// elliptical-arc:
/// ( "A" | "a" ) wsp* elliptical-arc-argument-sequence
ellipticalArc() =>
pp.pattern('Aa') & wsp().star() & ellipticalArcArgumentSequence();

/// elliptical-arc-argument-sequence:
/// elliptical-arc-argument
/// | elliptical-arc-argument comma-wsp? elliptical-arc-argument-sequence
ellipticalArcArgumentSequence() =>
(ellipticalArcArgument() &
commaWsp().optional() &
ref(ellipticalArcArgumentSequence))
| ellipticalArcArgument();

/// elliptical-arc-argument:
/// nonnegative-number comma-wsp? nonnegative-number comma-wsp?
/// number comma-wsp flag comma-wsp? flag comma-wsp? coordinate-pair
ellipticalArcArgument() =>
nonNegativeNumber() & commaWsp().optional() &
nonNegativeNumber() & commaWsp().optional() &
number() & commaWsp() &
flag() & commaWsp().optional() &
flag() & commaWsp().optional() &
coordinatePair();

/// coordinate-pair:
/// coordinate comma-wsp? coordinate
coordinatePair() =>
coordinate() & commaWhitespace().optional() & coordinate();
coordinate() & commaWsp().optional() & coordinate();

/// number
/// coordinate:
/// number
coordinate() => number();

/// integer-constant
/// | floating-point-constant
/// nonnegative-number:
/// integer-constant
/// | floating-point-constant
nonNegativeNumber() => floatingPointConstant() | integerConstant();

/// sign? integer-constant
/// | sign? floating-point-constant
/// number:
/// sign? integer-constant
/// | sign? floating-point-constant
number() =>
sign().optional() & floatingPointConstant()
| sign().optional() & integerConstant();

/// "0" | 1
flag() => pattern('01');
/// flag:
/// "0" | "1"
flag() => pp.pattern('01');

/// (wsp+ comma? wsp*) | (comma wsp*)
commaWhitespace() =>
(comma() & whitespace().star())
| (whitespace().plus() & comma().optional() & whitespace().star());
/// comma-wsp:
/// (wsp+ comma? wsp*) | (comma wsp*)
commaWsp() =>
(comma() & wsp().star())
| (wsp().plus() & comma().optional() & wsp().star());

/// ","
comma() => char(',');
/// comma:
/// ","
comma() => pp.char(',');

/// digit-sequence
/// integer-constant:
/// digit-sequence
integerConstant() => digitSequence();

/// fractional-constant exponent?
/// | digit-sequence exponent
/// floating-point-constant:
/// fractional-constant exponent?
/// | digit-sequence exponent
floatingPointConstant() =>
fractionalConstant() & exponent().optional()
| digitSequence() & exponent();

/// digit-sequence? "." digit-sequence
/// | digit-sequence "."
/// fractional-constant:
/// digit-sequence? "." digit-sequence
/// | digit-sequence "."
fractionalConstant() =>
(digitSequence().optional() & char('.') & digitSequence())
| (digitSequence() & char('.'));
(digitSequence().optional() & pp.char('.') & digitSequence())
| (digitSequence() & pp.char('.'));

/// ( "e" | "E" ) sign? digit-sequence
exponent() => pattern('eE') & sign().optional() & digitSequence();
/// exponent:
/// ( "e" | "E" ) sign? digit-sequence
exponent() => pp.pattern('eE') & sign().optional() & digitSequence();

/// "+" | "-"
sign() => pattern('+-');
/// sign:
/// "+" | "-"
sign() => pp.pattern('+-');

/// digit
/// | digit digit-sequence
/// digit-sequence:
/// digit
/// | digit digit-sequence
digitSequence() => digit().plus().flatten();

/// digit:
/// "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
digit() => pp.digit();

/// wsp:
/// (#x20 | #x9 | #xD | #xA)
wsp() =>
// Petiteparser whitespace parser is slightly more lenient
// than the SVG spec, but that should be all right.
pp.whitespace();
}
Loading