2011-02-25 4 views
9

Estoy escribiendo un programa en C# que esencialmente lee un archivo SVG, y hace algunas cosas útiles con los contenidos. Los datos más complejos con los que trabajaré son caminos. Se toman formas tales como este:Analizando los elementos de "ruta" de SVG con C# - ¿hay bibliotecas para hacer esto?

<path d="M5.4,3.806h6.336v43.276h20.738v5.256H5.4V3.806z"/> 

En este caso, el M, h, v, H, V, y z indican algunos comandos. En cierto modo, son como funciones, y los números que las siguen son argumentos. Hay también algunos otros más complejos:

<path d="M70.491,50.826c-2.232,1.152-6.913,2.304-12.817,2.304c-13.682,0-23.906-8.641-23.906-24.626 
     c0-15.266,10.297-25.49,25.346-25.49c5.977,0,9.865,1.296,11.521,2.16l-1.584,5.112C66.747,9.134,63.363,8.27,59.33,8.27 
     c-11.377,0-18.938,7.272-18.938,20.018c0,11.953,6.841,19.514,18.578,19.514c3.888,0,7.777-0.792,10.297-2.016L70.491,50.826z"/> 

En este caso, el comando "c" es seguido por 6 argumentos (-2.232, 1.152, -6.913, 2.304, -12.817, y 2,304 en el primer caso) . Puedes ver cómo esto puede ser complicado. Mi pregunta es esta: ¿la comunidad SO está al tanto de las bibliotecas existentes que leen estos datos en algunos ADT útiles?

Antes de dejar de codificar todo y escribir un montón de funciones de análisis de cadenas, me gustaría no reinventar la rueda. Además, cualquier consejo sería apreciado. Soy consciente de cómo leer un documento XML, ese no es el problema aquí.

+0

¿solo necesita transformar cada ruta en una Lista de comandos (donde cada comando tiene sus propios parámetros) o necesita construir un intérprete básico de esos comandos? –

+0

Ambos tendrán que suceder, y estoy buscando información sobre ambos. –

Respuesta

9

no sé de librerías específicas en C#, sin embargo, puede empezar por analizar este tipo de estructura como esta:

string path = "M5.4,3.806h6.336v43.276h20.738v5.256H5.4V3.806z"; 
string separators = @"(?=[MZLHVCSQTAmzlhvcsqta])"; // these letters are valid SVG 
          // commands. Whenever we find one, a new command is 
          // starting. Let's split the string there. 
var tokens = Regex.Split(path, separators).Where(t => !string.IsNullOrEmpty(t)); 

Ahora usted tiene una lista de comandos seguidos por sus argumentos. A continuación, puede proceder a dividir los argumentos de la misma manera.

Dijiste que los argumentos pueden separarse por un espacio, una coma o un signo menos (que, a diferencia de la coma y el espacio en blanco, debe seguir siendo parte de los argumentos), así que puedes usar otra expresión regular simple (nota que No soy fan de las expresiones regulares, pero en este caso creo que se suman a la legibilidad).

string argSeparators = @"[\s,]|(?=-)"; // discard whitespace and comma but keep the - 
var splitArgs = Regex 
    .Split(remainingargs, argSeparators) 
    .Where(t => !string.IsNullOrEmpty(t)); 

Me gustaría terminar con esto en una clase SVGCommand, como este

class SVGCommand 
{ 
    public char command {get; private set;} 
    public float[] arguments {get; private set;} 

    public SVGCommand(char command, params float[] arguments) 
    { 
     this.command=command; 
     this.arguments=arguments; 
    } 

    public static SVGCommand Parse(string SVGpathstring) 
    { 
     var cmd = SVGpathstring.Take(1).Single(); 
     string remainingargs = SVGpathstring.Substring(1); 

     string argSeparators = @"[\s,]|(?=-)"; 
     var splitArgs = Regex 
      .Split(remainingargs, argSeparators) 
      .Where(t => !string.IsNullOrEmpty(t)); 

     float[] floatArgs = splitArgs.Select(arg => float.Parse(arg)).ToArray(); 
     return new SVGCommand(cmd,floatArgs); 
    } 
} 

Ahora un simple "intérprete" podría ser algo como esto:

string path = "M70.491,50.826c-2.232,1.152-6.913,2.304-12.817,2.304c-13.682,0-23.906-8.641-23.906-24.626" + 
"c0-15.266,10.297-25.49,25.346-25.49c5.977,0,9.865,1.296,11.521,2.16l-1.584,5.112C66.747,9.134,63.363,8.27,59.33,8.27" + 
"c-11.377,0-18.938,7.272-18.938,20.018c0,11.953,6.841,19.514,18.578,19.514c3.888,0,7.777-0.792,10.297-2.016L70.491,50.826z"; 
    string separators = @"(?=[A-Za-z])"; 
    var tokens = Regex.Split(path, separators).Where(t => !string.IsNullOrEmpty(t)); 

    // our "interpreter". Runs the list of commands and does something for each of them. 
    foreach (string token in tokens){ 
        // note that Parse could throw an exception 
        // if the path is not correct 
     SVGCommand c = SVGCommand.Parse(token); 
     Console.WriteLine("doing something with command {0}", c.command); 
    } 

Si necesita hacer algo más sofisticado, F # es probablemente better suited for the job (y es interoperable con C#). No estoy sugiriendo que aprenda F # solo para esta tarea específica, solo pensé en mencionarlo, en caso de que ya lo esté buscando para otra cosa.

+0

Edité el original con un ejemplo. El problema con los argumentos es que pueden usar una coma como delimitador, o un espacio, o un signo menos. En el caso del signo menos, no es un "delimitador verdadero" ya que también es parte del argumento en sí mismo. –

+0

@ Adam Le he ampliado la respuesta en función de sus aclaraciones. Espero que ayude –

+0

hay otra trampa, el código no maneja los números en notación científica correctamente. por ejemplo, 1.78e-34 se dividiría en dos coordenadas. alguna idea de cómo atrapar ese caso con la expresión regular? – thalm

4

Sería posible hacer esto usando el objeto WPF Geometry. Por lo que puedo decir, el Path Markup syntax utilizado por WPF es la misma sintaxis que la ruta SVG.

var data = "M5.4,3.806h6.336v43.276h20.738v5.256H5.4V3.806z"; 

var geometry = Geometry.Parse(data); 

var pathGeometry = PathGeometry.CreateFromGeometry(geometry); 

foreach (var figure in pathGeometry.Figures) 
{ 
    // Do something interesting with each path figure. 
    foreach (var segment in figure.Segments) 
    { 
     // Do something interesting with each segment. 
    } 
} 
+1

Esto casi me estaba haciendo feliz hasta que descubrí que era una parte de System.Windows que no es compatible con Unity 3D debido a Mono. –

+0

Lo siento, no puedo ayudar, ya que no estoy familiarizado con Unity 3D – bstoney

+0

Sin embargo, esto teóricamente puede ser copiado del código fuente de .NET. –

Cuestiones relacionadas