【C#】SVG(XML)を読み込んで属性の値を取得する
C#でSVG(XML)を読み込んで属性の値を取得したい!
概要
今回の記事では、C#でSVG(XML)を読み込んで属性の値を取得する手順を掲載する。
仕様書
- .NET Core 3.1
手順書
サンプルとしてAffinity Designerで適当に作った下記のSVG(cs-get-attribute-from-svg.svg
)をC#で読み込んで、属性の値を取得してみる。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(0.25,0,0,0.444444,0,0)">
<rect id="アートボード1" x="0" y="0" width="1024" height="576" style="fill:none;"/>
<g id="アートボード11" serif:id="アートボード1">
<g id="矩形" transform="matrix(2.57286,0,0,1.9726,199.397,21.6986)">
<rect x="22" y="62" width="199" height="146" style="fill:rgb(13,121,242);"/>
</g>
<g id="円" transform="matrix(3.95876,0,0,2.20408,5.27835,5.87755)">
<ellipse cx="176.5" cy="177" rx="48.5" ry="49" style="fill:rgb(242,94,13);"/>
</g>
</g>
</g>
</svg>
このSVGには青い矩形と赤い円が含まれる。実際に表示すると下記のような感じ。
特定の属性の値を狙い撃ちで取得する
矩形の<g>
の中の<rect>
の属性x
と属性y
の値を取得して、コンソールに表示してみる。
using System;
using System.Xml;
class Program
{
static void Main(string[] args)
{
var xml = new XmlDocument();
xml.Load("cs-get-attribute-from-svg.svg");
var xmlNamespaceManager = new XmlNamespaceManager(xml.NameTable);
xmlNamespaceManager.AddNamespace("a", "http://www.w3.org/2000/svg");
var n = xml.SelectSingleNode("/a:svg/a:g/a:g/a:g/a:rect", xmlNamespaceManager);
Console.WriteLine($@"x = {n.Attributes["x"].InnerText}, y = {n.Attributes["y"].InnerText}");
}
}
実行すると下記のように表示される。
x = 22, y = 62
解説
普通のxmlでは必要ないっぽいんだけども、SVGでは色々処理するのに名前空間を追加する必要がある。下記の部分で名前空間を追加してる。"a"
が名前空間になる文字列。任意の文字列で良い。短いと楽。
var xmlNamespaceManager = new XmlNamespaceManager(xml.NameTable);
xmlNamespaceManager.AddNamespace("a", "http://www.w3.org/2000/svg");
xml.SelectSingleNode
メゾットでタグ(ノード)を指定する。第1引数の"/a:svg/a:g/a:g/a:g/a:rect"
がタグを指定してる部分になる。
svg
とg
とrect
はタグでこの場合は、<svg>
の中の<g>
の中の<g>
の中の<g>
の中の<rect>
を指定してる。
タグの前のa:
は名前空間で先程追加した名前空間の文字列を入力する。
タグの間は/
で区切る。
n.Attributes["x"].InnerText
の部分で属性の値を取得してる。
特定のタグに含まれる複数のタグの属性の値を取得する
アートボード1<g>
の中に含まれる<g>
の属性id
の値を出力してみる。再帰処理は無し。
using System;
using System.Xml;
class Program
{
var xml = new XmlDocument();
xml.Load("cs-get-attribute-from-svg.svg");
var xmlNamespaceManager = new XmlNamespaceManager(xml.NameTable);
xmlNamespaceManager.AddNamespace("a", "http://www.w3.org/2000/svg");
var nodes = xml.SelectNodes("/a:svg/a:g/a:g", xmlNamespaceManager);
foreach (XmlNode n in nodes)
{
if (n.Name == "g")
{
if (n.Attributes.GetNamedItem("id") != null))
{
Console.WriteLine(n.Attributes["id"].InnerText);
}
}
}
}
実行すると下記のように表示される。
矩形
円
解説
前述と異なる部分について解説する。
var nodes = xml.SelectNodes("/a:svg/a:g/a:g", xmlNamespaceManager);
の部分で読み込むタグの起点を指定する。
foreach
の中のif (n.Name == "g")
でタグの名前を取得して<g>
タグであるか判定する。この処理が結構重要でAffinity Designerで出力したSVGだとタグの間に#significant-whitespace
が含まれてて、これのAttributes
を読み取ろうとすると例外が発生しちゃうのでそれを防止してる。
if (n.Attributes.GetNamedItem("id") != null))
で指定の属性を持ってるか判定してる。
指定の属性を持ってない場合はnull
が返ってくる。
まとめ(感想文)
SVGの情報を取得する時に使えるかもね!
私はSVGで2次元の位置データを作ってて、それを読み込むのに使った。