使用C#解析SVG“path”元素 – 有没有库可以做到这一点?

我正在用C#编写一个基本上读取SVG文件的程序,并对内容做了一些有用的事情。 我将使用的最复杂的数据是路径。 他们采取如下forms:

 

在这种情况下,M,h,v,H,V和z表示一些命令。 在某种程度上,它们就像函数一样,跟随它们的数字是参数。 还有一些更复杂的:

  

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

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

我不知道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)); 

现在你有一个命令列表,后面跟着他们的参数。 然后,您可以以相同的方式继续拆分参数。

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

 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#可能更适合这项工作 (并且可以与C#互操作)。 我并不是建议只为这个特定的任务学习F#,我只是想我会提到它,以防你已经在考虑其他事情了。

可以使用WPF Geometry对象执行此操作。 据我所知,WPF使用的Path Markup语法与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. } }