2011-02-25 24 views
9

我正在用C#编写一个程序,它基本上读取一个SVG文件,并对内容做一些有用的事情。我将要处理的最复杂的数据是路径。他们采取如下形式:用C#解析SVG“路径”元素 - 有没有库可以做到这一点?

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

在这种情况下,M,H,V,H,V和Z表示一些命令。在某种程度上,它们就像函数一样,其后面的数字是参数。也有一些更复杂的:

<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"/> 

在这种情况下, “C” 命令后面6个参数(-2.232,1.152,-6.913,2.304,-12.817,和2.304在第一种情况下) 。你可以看到这可能会变得棘手。我的问题是:SO社区是否意识到任何现有的库将这些数据读入一些有用的ADT?

在我编写所有东西并编写大量字符串解析函数之前,我真的不想重新发明轮子。此外,任何意见将不胜感激。我知道如何阅读XML文档,这在这里不是问题。

+0

您是否只需要将每个路径转换为命令列表(其中每个命令都有自己的参数),还是需要构建这些命令的基本解释器? – 2011-02-25 09:18:56

+0

两者都需要发生,我正在寻求两方面的见解。 – 2011-02-25 15:15:27

回答

9

我不知道在C#中的特定库,但你可以通过分析这种结构是这样开始:

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)); 

现在你的命令,然后它们的参数列表。然后您可以继续以相同的方式分割参数。

你说的参数可以用空格,逗号或减号(与逗号和空格不同,应该保留为参数的一部分)分隔,所以你可以使用另一个简单的正则表达式(注意I我并不喜欢正则表达式,但在这种情况下,我认为它们增加了可读性)。

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

我会在SVGCommand类包装这个,像这样

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); 
    } 
} 

现在,一个简单的“解释”可能是这个样子:

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); 
    } 

如果您需要做的事情更复杂的F#可能是better suited for the job(并且可以与C#互操作)。我并不是建议仅仅为了这个特定的任务学习F#,我只是想我会提及它,以防你已经为其他东西寻找它。

+0

我用一个例子编辑了原文。争论的麻烦是他们可以使用逗号作为分隔符,空格或减号。在减号的情况下,它不是“真正的分隔符”,因为它也是参数本身的一部分。 – 2011-02-25 15:17:46

+0

@Adam我根据你的澄清扩大了答案。希望它有助于 – 2011-02-26 08:07:00

+0

还有另一个缺陷,代码不能正确处理科学记数法中的数字。例如1.78e-34会分成两个坐标。任何想法如何用正则表达式来捕获这种情况? – thalm 2012-07-16 18:46:58

4

使用WPF几何对象可以做到这一点。据我所知,WPF使用的Path Markup syntax与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

这几乎只是我的一天,直到我发现它是System.Windows的一部分,因为Mono,它在Unity 3D中不受支持。 – 2017-02-12 18:34:19

+0

对不起,我不能帮助那里,因为我不熟悉Unity 3D – bstoney 2017-02-13 09:57:59

+0

虽然这在理论上可以从.NET源代码中删除。 – 2017-03-11 20:39:19