private static Boolean MarkState(Boolean hasMode,
                                  ref CoordinateParserState state)
 {
     state.HasMore = hasMode;
     ++state.CharsPosition;
     return(hasMode);
 }
Example #2
0
        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));
            }
        }
Example #5
0
        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;
            }
        }