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

190 lines
4.4 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 );
// RJLog.Log( "Processing", d[ i ], ">>", lowerCase, " ends ", commandEnd );
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 ] + "";
pc.pathIndex = commands.Count;
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 parameters = d.Substring( start, ( end + 1 ) - start );
parameters = parameters.Trim();
// RJLog.Log( "Parsing parameters:", parameters );
var regex = new Regex( " |," );
var splitted = RegexUtility.Split( parameters, " |," );
splitted.ForEach(
( s )=>
{
var number = RegexUtility.ParseFloat( s.Trim() );
command.paramaters.Add( number );
// RJLog.Log( s, ">>", number );
}
);
}
void ReadParametersOld( 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;
}
}
}
}