2011-02-25 48 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

相關問題