private static Boolean MarkState(Boolean hasMode, ref CoordinateParserState state) { state.HasMore = hasMode; ++state.CharsPosition; return(hasMode); }
private ISvgImage Parse(Double width, Double height, String path) { var segments = new SvgPathSegmentList(); var pathTrimmed = path.TrimEnd(); var commandStart = 0; var pathLength = pathTrimmed.Length; for (var i = 0; i < pathLength; ++i) { var currentChar = pathTrimmed[i]; if (Char.IsLetter(currentChar) && currentChar != 'e' && currentChar != 'E' ) // e is used in scientific notiation. but not svg path { var start = commandStart; var length = i - commandStart; var command = pathTrimmed.Substring(start, length).Trim(); commandStart = i; if (command.Length > 0) { var commandSetTrimmed = pathTrimmed.Substring(start, length).Trim(); var state = new CoordinateParserState(ref commandSetTrimmed); CreatePathSegments(commandSetTrimmed[0], segments, state, commandSetTrimmed); } if (pathLength == i + 1) { var commandSetTrimmed = pathTrimmed.Substring(i, 1).Trim(); var state = new CoordinateParserState(ref commandSetTrimmed); CreatePathSegments(commandSetTrimmed[0], segments, state, commandSetTrimmed); } } else if (pathLength == i + 1) { var start = commandStart; var length = i - commandStart + 1; var command = pathTrimmed.Substring(start, length).Trim(); if (command.Length > 0) { var commandSetTrimmed = pathTrimmed.Substring(start, length).Trim(); var state = new CoordinateParserState(ref commandSetTrimmed); CreatePathSegments(commandSetTrimmed[0], segments, state, commandSetTrimmed); } } } return(new SvgImage(width, height, segments, _imageProvider)); }
//public static Boolean TryGetBool(out Boolean result, // ref ReadOnlySpan<Char> chars, // ref CoordinateParserState state) //{ // var charsLength = chars.Length; // while (state.CharsPosition < charsLength && state.HasMore) // { // switch (state.CurrNumState) // { // case NumState.Separator: // var currentChar = chars[state.CharsPosition]; // if (IsCoordSeparator(currentChar)) // state.NewNumState = NumState.Separator; // else if (currentChar == '0') // { // result = false; // state.NewNumState = NumState.Separator; // state.Position = state.CharsPosition + 1; // return MarkState(true, ref state); // } // else if (currentChar == '1') // { // result = true; // state.NewNumState = NumState.Separator; // state.Position = state.CharsPosition + 1; // return MarkState(true, ref state); // } // else // { // result = false; // return MarkState(false, ref state); // } // break; // default: // result = false; // return MarkState(false, ref state); // } // ++state.CharsPosition; // } // result = false; // return MarkState(false, ref state); //} public static Boolean TryGetBool(out Boolean result, ref String chars, ref CoordinateParserState state) { var charsLength = chars.Length; while (state.CharsPosition < charsLength && state.HasMore) { switch (state.CurrNumState) { case NumState.Separator: var currentChar = chars[state.CharsPosition]; if (IsCoordSeparator(currentChar)) { state.NewNumState = NumState.Separator; } else if (currentChar == '0') { result = false; state.NewNumState = NumState.Separator; state.Position = state.CharsPosition + 1; return(MarkState(true, ref state)); } else if (currentChar == '1') { result = true; state.NewNumState = NumState.Separator; state.Position = state.CharsPosition + 1; return(MarkState(true, ref state)); } else { result = false; return(MarkState(false, ref state)); } break; default: result = false; return(MarkState(false, ref state)); } ++state.CharsPosition; } result = false; return(MarkState(false, ref state)); }
//public static Boolean TryGetFloat(out Single result, // ref ReadOnlySpan<Char> chars, // ref CoordinateParserState state) //{ // var charsLength = chars.Length; // while (state.CharsPosition < charsLength && state.HasMore) // { // var currentChar = chars[state.CharsPosition]; // switch (state.CurrNumState) // { // case NumState.Separator: // if (Char.IsNumber(currentChar)) // state.NewNumState = NumState.Integer; // else if (IsCoordSeparator(currentChar)) // state.NewNumState = NumState.Separator; // else // switch (currentChar) // { // case '.': // state.NewNumState = NumState.DecPlace; // break; // case '+': // case '-': // state.NewNumState = NumState.Prefix; // break; // default: // state.NewNumState = NumState.Invalid; // break; // } // break; // case NumState.Prefix: // if (Char.IsNumber(currentChar)) // state.NewNumState = NumState.Integer; // else if (currentChar == '.') // state.NewNumState = NumState.DecPlace; // else // state.NewNumState = NumState.Invalid; // break; // case NumState.Integer: // if (Char.IsNumber(currentChar)) // state.NewNumState = NumState.Integer; // else if (IsCoordSeparator(currentChar)) // state.NewNumState = NumState.Separator; // else // switch (currentChar) // { // case '.': // state.NewNumState = NumState.DecPlace; // break; // case 'E': // case 'e': // state.NewNumState = NumState.Exponent; // break; // case '+': // case '-': // state.NewNumState = NumState.Prefix; // break; // default: // state.NewNumState = NumState.Invalid; // break; // } // break; // case NumState.DecPlace: // if (Char.IsNumber(currentChar)) // state.NewNumState = NumState.Fraction; // else if (IsCoordSeparator(currentChar)) // state.NewNumState = NumState.Separator; // else // switch (currentChar) // { // case 'E': // case 'e': // state.NewNumState = NumState.Exponent; // break; // case '+': // case '-': // state.NewNumState = NumState.Prefix; // break; // default: // state.NewNumState = NumState.Invalid; // break; // } // break; // case NumState.Fraction: // if (Char.IsNumber(currentChar)) // state.NewNumState = NumState.Fraction; // else if (IsCoordSeparator(currentChar)) // state.NewNumState = NumState.Separator; // else // switch (currentChar) // { // case '.': // state.NewNumState = NumState.DecPlace; // break; // case 'E': // case 'e': // state.NewNumState = NumState.Exponent; // break; // case '+': // case '-': // state.NewNumState = NumState.Prefix; // break; // default: // state.NewNumState = NumState.Invalid; // break; // } // break; // case NumState.Exponent: // if (Char.IsNumber(currentChar)) // state.NewNumState = NumState.ExpValue; // else if (IsCoordSeparator(currentChar)) // state.NewNumState = NumState.Invalid; // else // switch (currentChar) // { // case '+': // case '-': // state.NewNumState = NumState.ExpPrefix; // break; // default: // state.NewNumState = NumState.Invalid; // break; // } // break; // case NumState.ExpPrefix: // if (Char.IsNumber(currentChar)) // state.NewNumState = NumState.ExpValue; // else // state.NewNumState = NumState.Invalid; // break; // case NumState.ExpValue: // if (Char.IsNumber(currentChar)) // state.NewNumState = NumState.ExpValue; // else if (IsCoordSeparator(currentChar)) // state.NewNumState = NumState.Separator; // else // switch (currentChar) // { // case '.': // state.NewNumState = NumState.DecPlace; // break; // case '+': // case '-': // state.NewNumState = NumState.Prefix; // break; // default: // state.NewNumState = NumState.Invalid; // break; // } // break; // } // if (state.CurrNumState != NumState.Separator && state.NewNumState < state.CurrNumState) // { // var value = chars.Slice(state.Position, state.CharsPosition - state.Position); // result = StringParser.ToFloat(ref value); // state.Position = state.CharsPosition; // state.CurrNumState = state.NewNumState; // return MarkState(true, ref state); // } // if (state.NewNumState != state.CurrNumState && state.CurrNumState == NumState.Separator) // state.Position = state.CharsPosition; // if (state.NewNumState == NumState.Invalid) // { // result = Single.MinValue; // return MarkState(false, ref state); // } // state.CurrNumState = state.NewNumState; // ++state.CharsPosition; // } // if (state.CurrNumState == NumState.Separator || !state.HasMore || state.Position >= charsLength) // { // result = Single.MinValue; // return MarkState(false, ref state); // } // { // var value = chars.Slice(state.Position, charsLength - state.Position); // result = StringParser.ToFloat(ref value); // state.Position = charsLength; // return MarkState(true, ref state); // } //} public static Boolean TryGetFloat(out Single result, ref String chars, ref CoordinateParserState state) { var charsLength = chars.Length; while (state.CharsPosition < charsLength && state.HasMore) { var currentChar = chars[state.CharsPosition]; switch (state.CurrNumState) { case NumState.Separator: if (Char.IsNumber(currentChar)) { state.NewNumState = NumState.Integer; } else if (IsCoordSeparator(currentChar)) { state.NewNumState = NumState.Separator; } else { switch (currentChar) { case '.': state.NewNumState = NumState.DecPlace; break; case '+': case '-': state.NewNumState = NumState.Prefix; break; default: state.NewNumState = NumState.Invalid; break; } } break; case NumState.Prefix: if (Char.IsNumber(currentChar)) { state.NewNumState = NumState.Integer; } else if (currentChar == '.') { state.NewNumState = NumState.DecPlace; } else { state.NewNumState = NumState.Invalid; } break; case NumState.Integer: if (Char.IsNumber(currentChar)) { state.NewNumState = NumState.Integer; } else if (IsCoordSeparator(currentChar)) { state.NewNumState = NumState.Separator; } else { switch (currentChar) { case '.': state.NewNumState = NumState.DecPlace; break; case 'E': case 'e': state.NewNumState = NumState.Exponent; break; case '+': case '-': state.NewNumState = NumState.Prefix; break; default: state.NewNumState = NumState.Invalid; break; } } break; case NumState.DecPlace: if (Char.IsNumber(currentChar)) { state.NewNumState = NumState.Fraction; } else if (IsCoordSeparator(currentChar)) { state.NewNumState = NumState.Separator; } else { switch (currentChar) { case 'E': case 'e': state.NewNumState = NumState.Exponent; break; case '+': case '-': state.NewNumState = NumState.Prefix; break; default: state.NewNumState = NumState.Invalid; break; } } break; case NumState.Fraction: if (Char.IsNumber(currentChar)) { state.NewNumState = NumState.Fraction; } else if (IsCoordSeparator(currentChar)) { state.NewNumState = NumState.Separator; } else { switch (currentChar) { case '.': state.NewNumState = NumState.DecPlace; break; case 'E': case 'e': state.NewNumState = NumState.Exponent; break; case '+': case '-': state.NewNumState = NumState.Prefix; break; default: state.NewNumState = NumState.Invalid; break; } } break; case NumState.Exponent: if (Char.IsNumber(currentChar)) { state.NewNumState = NumState.ExpValue; } else if (IsCoordSeparator(currentChar)) { state.NewNumState = NumState.Invalid; } else { switch (currentChar) { case '+': case '-': state.NewNumState = NumState.ExpPrefix; break; default: state.NewNumState = NumState.Invalid; break; } } break; case NumState.ExpPrefix: if (Char.IsNumber(currentChar)) { state.NewNumState = NumState.ExpValue; } else { state.NewNumState = NumState.Invalid; } break; case NumState.ExpValue: if (Char.IsNumber(currentChar)) { state.NewNumState = NumState.ExpValue; } else if (IsCoordSeparator(currentChar)) { state.NewNumState = NumState.Separator; } else { switch (currentChar) { case '.': state.NewNumState = NumState.DecPlace; break; case '+': case '-': state.NewNumState = NumState.Prefix; break; default: state.NewNumState = NumState.Invalid; break; } } break; } if (state.CurrNumState != NumState.Separator && state.NewNumState < state.CurrNumState) { var value = chars.Substring(state.Position, state.CharsPosition - state.Position); result = Single.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture); //result = StringParser.ToFloat(ref value); state.Position = state.CharsPosition; state.CurrNumState = state.NewNumState; return(MarkState(true, ref state)); } if (state.NewNumState != state.CurrNumState && state.CurrNumState == NumState.Separator) { state.Position = state.CharsPosition; } if (state.NewNumState == NumState.Invalid) { result = Single.MinValue; return(MarkState(false, ref state)); } state.CurrNumState = state.NewNumState; ++state.CharsPosition; } if (state.CurrNumState == NumState.Separator || !state.HasMore || state.Position >= charsLength) { result = Single.MinValue; return(MarkState(false, ref state)); } { var value = chars.Substring(state.Position, charsLength - state.Position); //result = StringParser.ToFloat(ref value); result = Single.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture); state.Position = charsLength; return(MarkState(true, ref state)); } }
private static void CreatePathSegments(Char command, SvgPathSegmentList segments, CoordinateParserState state, String chars) { var isRelative = Char.IsLower(command); // http://www.w3.org/TR/SVG11/paths.html#PathDataGeneralInformation switch (command) { case 'M': // moveto case 'm': // relative moveto { if (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state)) { var mov = new SvgMoveToSegment( ToAbsolute(coords0, coords1, segments, isRelative)); segments.Add(mov); //yield return mov; } while (CoordinateParser.TryGetFloat(out coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out coords1, ref chars, ref state)) { var line = new SvgLineSegment(segments.Last.End, ToAbsolute(coords0, coords1, segments, isRelative)); segments.Add(line); //yield return line; } } break; case 'A': // elliptical arc case 'a': // relative elliptical arc { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords2, ref chars, ref state) && CoordinateParser.TryGetBool(out var size, ref chars, ref state) && CoordinateParser.TryGetBool(out var sweep, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords3, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords4, ref chars, ref state)) { // A|a rx ry x-axis-rotation large-arc-flag sweep-flag x y var arc = new SvgArcSegment( segments.Last.End, coords0, coords1, coords2, size ? SvgArcSize.Large : SvgArcSize.Small, sweep ? SvgArcSweep.Positive : SvgArcSweep.Negative, ToAbsolute(coords3, coords4, segments, isRelative)); segments.Add(arc); //yield return arc; } } break; case 'L': // lineto case 'l': // relative lineto { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state)) { var line = new SvgLineSegment( segments.Last.End, ToAbsolute(coords0, coords1, segments, isRelative)); segments.Add(line); //yield return line; } } break; case 'H': // horizontal lineto case 'h': // relative horizontal lineto { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state)) { var line = new SvgLineSegment( segments.Last.End, ToAbsolute(coords0, segments.Last.End.Y, segments, isRelative, false)); segments.Add(line); //yield return line; } } break; case 'V': // vertical lineto case 'v': // relative vertical lineto { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state)) { var line = new SvgLineSegment( segments.Last.End, ToAbsolute(segments.Last.End.X, coords0, segments, false, isRelative)); segments.Add(line); //yield return line; } } break; case 'Q': // quadratic bézier curveto case 'q': // relative quadratic bézier curveto { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords2, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords3, ref chars, ref state)) { var quad = new SvgQuadraticCurveSegment( segments.Last.End, ToAbsolute(coords0, coords1, segments, isRelative), ToAbsolute(coords2, coords3, segments, isRelative)); segments.Add(quad); //yield return quad; } } break; case 'T': // shorthand/smooth quadratic bézier curveto case 't': // relative shorthand/smooth quadratic bézier curveto { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state)) { var lastQuadCurve = segments.Last as SvgQuadraticCurveSegment; var controlPoint = lastQuadCurve != null ? Reflect(lastQuadCurve.ControlPoint, segments.Last.End) : segments.Last.End; var quad = new SvgQuadraticCurveSegment( segments.Last.End, controlPoint, ToAbsolute(coords0, coords1, segments, isRelative)); segments.Add(quad); //yield return quad; } } break; case 'C': // curveto case 'c': // relative curveto { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords2, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords3, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords4, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords5, ref chars, ref state)) { var cube = new SvgCubicCurveSegment( segments.Last.End, ToAbsolute(coords0, coords1, segments, isRelative), ToAbsolute(coords2, coords3, segments, isRelative), ToAbsolute(coords4, coords5, segments, isRelative)); segments.Add(cube); //yield return cube; } } break; case 'S': // shorthand/smooth curveto case 's': // relative shorthand/smooth curveto { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords2, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords3, ref chars, ref state)) { var lastCubicCurve = segments.Last as SvgCubicCurveSegment; var controlPoint = lastCubicCurve != null ? Reflect(lastCubicCurve.SecondControlPoint, segments.Last.End) : segments.Last.End; var cube = new SvgCubicCurveSegment( segments.Last.End, controlPoint, ToAbsolute(coords0, coords1, segments, isRelative), ToAbsolute(coords2, coords3, segments, isRelative)); segments.Add(cube); //yield return cube; } } break; case 'Z': // closepath case 'z': // relative closepath { var bye = new SvgClosePathSegment(); segments.Add(bye); //yield return bye; } break; } }