rj-action-library/Runtime/XML/SVG/SVGPathParser.cs

164 lines
3.7 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Rokojori
{
public class SVGPathParser
{
public static readonly string SVGPathCommands = "mlhvcsqtaz";
List<SVGPathCommand> commands = new List<SVGPathCommand>();
List<Message> messages = new List<Message>();
public static List<SVGPathCommand> Parse( string d )
{
var svgPath = new SVGPathParser();
var commands = svgPath.ParseCommands( d );
if ( Messages.HasError( svgPath.messages ) )
{
return null;
}
return commands;
}
public static List<Message> GetMessages( string d )
{
var svgPath = new SVGPathParser();
var commands = svgPath.ParseCommands( d );
return svgPath.messages;
}
public List<SVGPathCommand> ParseCommands( string d )
{
for ( int i = 0; i < d.Length; i++ )
{
if ( d[ i ] == ' ' )
{
continue;
}
var lowerCase = ( d[ i ] + "" ).ToLower();
var commandIndex = SVGPathCommands.IndexOf( lowerCase );
if ( commandIndex == -1 )
{
Messages.Error( messages, "Unknown command at '" + i + "'. Found " + d[ i ] );
return null;
}
var commandEnd = ProcessCommand( d, i );
if ( commandEnd == -1 || Messages.HasError( messages ) )
{
Messages.Error( messages, "Command couldn't be processed at '" + i + "'. Processed " + d[ i ] );
return null;
}
i = commandEnd;
}
return commands;
}
int ProcessCommand( string d, int offset)
{
var end = FindEnd( d, offset + 1 );
var pc = new SVGPathCommand();
pc.type = d[ offset ] + "";
ReadParameters( pc, d, offset + 1, end );
commands.Add( pc );
return end;
}
int FindEnd( string d, int offset )
{
for ( int i = offset; i < d.Length; i++ )
{
var chr = ( d[ i ] + "" ).ToLower();
if ( SVGPathParser.SVGPathCommands.IndexOf( chr ) != - 1 )
{
return i - 1;
}
}
return d.Length - 1;
}
void ReadParameters( SVGPathCommand command, string d, int start, int end )
{
var matcher = LexerMatcherLibrary.NumberMatcher;
var offset = start;
var isNegative = false;
while ( offset <= end )
{
if ( d[ offset ] == ' ' || d[ offset ] == ',' )
{
offset ++;
isNegative = false;
continue;
}
if ( d[ offset ] == '-' )
{
offset ++;
isNegative = true;
continue;
}
var matchLength = matcher.MatchLength( d, offset );
if ( matchLength == -1 )
{
Messages.Error( messages,
"Found no number." +
" Offset:" + offset +
" Start: " + start +
" End: " + end +
" Part: " + "'" + d.Substring( start, end - start ) + "'"
);
return;
}
var endIndex = offset + matchLength;
if ( endIndex > end )
{
Messages.Error( messages,
"Number parsing exceeded expected end." +
" Offset:" + offset +
" Start: " + start +
" End: " + end +
" Match End: " + endIndex +
" Part: " + "'" + d.Substring( start, end - start ) + "'"
);
return;
}
var number = RegexUtility.ParseFloat( d.Substring( offset, matchLength ) );
if ( isNegative )
{
number = -number;
}
offset += matchLength;
command.paramaters.Add( number );
isNegative = false;
}
}
}
}