【C#】SVG(XML)を読み込んで属性の値を取得する

ネコニウム研究所

PCを利用したモノづくりに関連する情報や超個人的なナレッジを掲載するブログ

【C#】SVG(XML)を読み込んで属性の値を取得する

2023-1-5 | , ,

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"がタグを指定してる部分になる。

svggrectはタグでこの場合は、<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次元の位置データを作ってて、それを読み込むのに使った。