From c4e12a4bf5331326b1c986f18f2cbb8721f12fe9 Mon Sep 17 00:00:00 2001 From: Rafael Teixeira Date: Fri, 22 Nov 2024 19:06:50 -0300 Subject: [PATCH] v3.5.0 - Use SearchValues from .NET8 and up Some hacks to deal with non-standard namespaces Build for .NET 9 too --- README.md | 2 +- SvgDocTest/DocForm.Designer.cs | 221 +++++++++++++-------- SvgDocTest/DocForm.cs | 20 +- SvgDocTest/Extensions.cs | 11 + SvgDotNetCoreTest/SvgDotNetCoreTest.csproj | 2 +- SvgNet/Elements/SvgElement.cs | 42 ++-- SvgNet/Elements/SvgStyledElement.cs | 47 +++-- SvgNet/Extensions/StringExtensions.cs | 30 +-- SvgNet/SvgFactory.cs | 9 + SvgNet/SvgNet.csproj | 8 +- SvgNetUnitTests/SvgNetUnitTests.csproj | 2 +- 11 files changed, 242 insertions(+), 152 deletions(-) create mode 100644 SvgDocTest/Extensions.cs diff --git a/README.md b/README.md index 40ccca5..97ae77d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A fork of the SvgNet & SvgGdi bridge (http://www.codeproject.com/KB/cs/svgnet.as __SvgNet is now available as a Nuget:__ [SvgNet](https://www.nuget.org/packages/SvgNet/). -__Latest version 3.4.0 is .NET Standard 2.0 and 2.1 and also .NET 8.0 (base and Windows) compatible and works with .NET Core 2.x and 3.x and .NET 5.0/6.0/7.0/8.0, but now requires .NET Framework 4.6.2 or higher__ +__Latest version 3.5.0 is .NET Standard 2.0 and 2.1 and also .NET 8.0 (base and Windows) compatible and works with .NET Core 2.x and 3.x and .NET 5.0/6.0/7.0/8.0, but now requires .NET Framework 4.6.2 or higher__ To build this version properly you need .NET 8.0.403+ SDK installed diff --git a/SvgDocTest/DocForm.Designer.cs b/SvgDocTest/DocForm.Designer.cs index ff9e762..fc8a398 100644 --- a/SvgDocTest/DocForm.Designer.cs +++ b/SvgDocTest/DocForm.Designer.cs @@ -23,135 +23,194 @@ protected override void Dispose(bool disposing) { /// the contents of this method with the code editor. /// private void InitializeComponent() { - this.svgOut = new System.Windows.Forms.WebBrowser(); this.svgIn = new System.Windows.Forms.WebBrowser(); - this.button2 = new System.Windows.Forms.Button(); - this.label2 = new System.Windows.Forms.Label(); - this.label1 = new System.Windows.Forms.Label(); - this.button3 = new System.Windows.Forms.Button(); this.tbIn = new System.Windows.Forms.TextBox(); + this.panelBottom = new System.Windows.Forms.GroupBox(); this.tbOut = new System.Windows.Forms.TextBox(); + this.svgOut = new System.Windows.Forms.WebBrowser(); + this.panelTop = new System.Windows.Forms.GroupBox(); + this.panelButtons = new System.Windows.Forms.Panel(); + this.button2 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); this.button1 = new System.Windows.Forms.Button(); + this.panelBottom.SuspendLayout(); + this.panelTop.SuspendLayout(); + this.panelButtons.SuspendLayout(); this.SuspendLayout(); // - // svgOut - // - this.svgOut.Location = new System.Drawing.Point(404, 309); - this.svgOut.MinimumSize = new System.Drawing.Size(20, 20); - this.svgOut.Name = "svgOut"; - this.svgOut.Size = new System.Drawing.Size(495, 232); - this.svgOut.TabIndex = 20; - // // svgIn // - this.svgIn.Location = new System.Drawing.Point(404, 41); - this.svgIn.MinimumSize = new System.Drawing.Size(20, 20); + this.svgIn.AllowWebBrowserDrop = false; + this.svgIn.Location = new System.Drawing.Point(760, 28); + this.svgIn.Margin = new System.Windows.Forms.Padding(4); + this.svgIn.MinimumSize = new System.Drawing.Size(27, 25); this.svgIn.Name = "svgIn"; - this.svgIn.Size = new System.Drawing.Size(495, 230); + this.svgIn.Size = new System.Drawing.Size(857, 420); this.svgIn.TabIndex = 19; // + // tbIn + // + this.tbIn.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.tbIn.Dock = System.Windows.Forms.DockStyle.Left; + this.tbIn.Font = new System.Drawing.Font("Cascadia Code", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tbIn.Location = new System.Drawing.Point(3, 28); + this.tbIn.Margin = new System.Windows.Forms.Padding(4); + this.tbIn.Multiline = true; + this.tbIn.Name = "tbIn"; + this.tbIn.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.tbIn.Size = new System.Drawing.Size(757, 415); + this.tbIn.TabIndex = 14; + this.tbIn.Text = ""; + // + // panelBottom + // + this.panelBottom.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.panelBottom.BackColor = System.Drawing.Color.LightSteelBlue; + this.panelBottom.Controls.Add(this.tbOut); + this.panelBottom.Controls.Add(this.svgOut); + this.panelBottom.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panelBottom.Font = new System.Drawing.Font("Calibri", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.panelBottom.Location = new System.Drawing.Point(0, 526); + this.panelBottom.Name = "panelBottom"; + this.panelBottom.Size = new System.Drawing.Size(1620, 451); + this.panelBottom.TabIndex = 21; + this.panelBottom.TabStop = false; + this.panelBottom.Text = "Output"; + // + // tbOut + // + this.tbOut.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.tbOut.Dock = System.Windows.Forms.DockStyle.Left; + this.tbOut.Font = new System.Drawing.Font("Cascadia Code", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tbOut.Location = new System.Drawing.Point(3, 28); + this.tbOut.Margin = new System.Windows.Forms.Padding(4); + this.tbOut.Multiline = true; + this.tbOut.Name = "tbOut"; + this.tbOut.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.tbOut.Size = new System.Drawing.Size(757, 420); + this.tbOut.TabIndex = 23; + this.tbOut.Text = ""; + // + // svgOut + // + this.svgOut.AllowWebBrowserDrop = false; + this.svgOut.Location = new System.Drawing.Point(760, 28); + this.svgOut.Margin = new System.Windows.Forms.Padding(4); + this.svgOut.MinimumSize = new System.Drawing.Size(27, 25); + this.svgOut.Name = "svgOut"; + this.svgOut.Size = new System.Drawing.Size(857, 420); + this.svgOut.TabIndex = 24; + // + // panelTop + // + this.panelTop.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.panelTop.BackColor = System.Drawing.Color.LightCoral; + this.panelTop.Controls.Add(this.svgIn); + this.panelTop.Controls.Add(this.tbIn); + this.panelTop.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panelTop.Font = new System.Drawing.Font("Calibri", 12F, System.Drawing.FontStyle.Bold); + this.panelTop.Location = new System.Drawing.Point(0, 80); + this.panelTop.Name = "panelTop"; + this.panelTop.Size = new System.Drawing.Size(1620, 446); + this.panelTop.TabIndex = 22; + this.panelTop.TabStop = false; + this.panelTop.Text = "Input"; + // + // panelButtons + // + this.panelButtons.BackColor = System.Drawing.Color.Khaki; + this.panelButtons.Controls.Add(this.button2); + this.panelButtons.Controls.Add(this.button3); + this.panelButtons.Controls.Add(this.button1); + this.panelButtons.Dock = System.Windows.Forms.DockStyle.Top; + this.panelButtons.Location = new System.Drawing.Point(0, 0); + this.panelButtons.Name = "panelButtons"; + this.panelButtons.Size = new System.Drawing.Size(1620, 74); + this.panelButtons.TabIndex = 23; + // // button2 // + this.button2.BackColor = System.Drawing.Color.White; + this.button2.FlatAppearance.BorderColor = System.Drawing.Color.Navy; + this.button2.FlatAppearance.BorderSize = 2; + this.button2.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.button2.Font = new System.Drawing.Font("Cascadia Code", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.button2.ForeColor = System.Drawing.Color.DarkMagenta; - this.button2.Location = new System.Drawing.Point(549, 3); + this.button2.Location = new System.Drawing.Point(603, 13); + this.button2.Margin = new System.Windows.Forms.Padding(4); this.button2.Name = "button2"; - this.button2.Size = new System.Drawing.Size(242, 32); - this.button2.TabIndex = 18; + this.button2.Size = new System.Drawing.Size(323, 39); + this.button2.TabIndex = 21; this.button2.Text = "Run Composition Tests"; + this.button2.UseVisualStyleBackColor = false; this.button2.Click += new System.EventHandler(this.Button2_Click); // - // label2 - // - this.label2.Font = new System.Drawing.Font("Cascadia Code", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label2.Location = new System.Drawing.Point(12, 285); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(136, 24); - this.label2.TabIndex = 17; - this.label2.Text = "Output:"; - // - // label1 - // - this.label1.Font = new System.Drawing.Font("Cascadia Code", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label1.Location = new System.Drawing.Point(12, 19); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(136, 24); - this.label1.TabIndex = 16; - this.label1.Text = "Input:"; - // // button3 // + this.button3.BackColor = System.Drawing.Color.White; + this.button3.FlatAppearance.BorderColor = System.Drawing.Color.Navy; + this.button3.FlatAppearance.BorderSize = 2; + this.button3.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.button3.Font = new System.Drawing.Font("Cascadia Code", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.button3.ForeColor = System.Drawing.Color.DarkMagenta; - this.button3.Location = new System.Drawing.Point(307, 3); + this.button3.Location = new System.Drawing.Point(1069, 13); + this.button3.Margin = new System.Windows.Forms.Padding(4); this.button3.Name = "button3"; - this.button3.Size = new System.Drawing.Size(242, 32); - this.button3.TabIndex = 15; + this.button3.Size = new System.Drawing.Size(323, 39); + this.button3.TabIndex = 20; this.button3.Text = "Run Type Tests"; + this.button3.UseVisualStyleBackColor = false; this.button3.Click += new System.EventHandler(this.Button3_Click); // - // tbIn - // - this.tbIn.Font = new System.Drawing.Font("Cascadia Code", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.tbIn.Location = new System.Drawing.Point(12, 41); - this.tbIn.Multiline = true; - this.tbIn.Name = "tbIn"; - this.tbIn.Size = new System.Drawing.Size(384, 232); - this.tbIn.TabIndex = 14; - this.tbIn.Text = ""; - // - // tbOut - // - this.tbOut.Font = new System.Drawing.Font("Cascadia Code", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.tbOut.Location = new System.Drawing.Point(12, 309); - this.tbOut.Multiline = true; - this.tbOut.Name = "tbOut"; - this.tbOut.Size = new System.Drawing.Size(384, 232); - this.tbOut.TabIndex = 13; - this.tbOut.Text = "textBox1"; - // // button1 // + this.button1.BackColor = System.Drawing.Color.White; + this.button1.FlatAppearance.BorderColor = System.Drawing.Color.Navy; + this.button1.FlatAppearance.BorderSize = 2; + this.button1.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.button1.Font = new System.Drawing.Font("Cascadia Code", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.button1.ForeColor = System.Drawing.Color.DarkMagenta; - this.button1.Location = new System.Drawing.Point(65, 3); + this.button1.Location = new System.Drawing.Point(137, 13); + this.button1.Margin = new System.Windows.Forms.Padding(4); this.button1.Name = "button1"; - this.button1.Size = new System.Drawing.Size(242, 32); - this.button1.TabIndex = 12; + this.button1.Size = new System.Drawing.Size(323, 39); + this.button1.TabIndex = 19; this.button1.Text = "Test an SVG file"; + this.button1.UseVisualStyleBackColor = false; this.button1.Click += new System.EventHandler(this.Button1_Click); // // DocForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(911, 555); - this.Controls.Add(this.svgOut); - this.Controls.Add(this.svgIn); - this.Controls.Add(this.button2); - this.Controls.Add(this.label2); - this.Controls.Add(this.button3); - this.Controls.Add(this.tbIn); - this.Controls.Add(this.tbOut); - this.Controls.Add(this.button1); - this.Controls.Add(this.label1); + this.AutoSize = true; + this.ClientSize = new System.Drawing.Size(1620, 977); + this.Controls.Add(this.panelButtons); + this.Controls.Add(this.panelTop); + this.Controls.Add(this.panelBottom); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Margin = new System.Windows.Forms.Padding(4); this.Name = "DocForm"; this.Text = "DocForm"; + this.panelBottom.ResumeLayout(false); + this.panelBottom.PerformLayout(); + this.panelTop.ResumeLayout(false); + this.panelTop.PerformLayout(); + this.panelButtons.ResumeLayout(false); this.ResumeLayout(false); - this.PerformLayout(); } #endregion - - private System.Windows.Forms.WebBrowser svgOut; private System.Windows.Forms.WebBrowser svgIn; - private System.Windows.Forms.Button button2; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.Label label1; - private System.Windows.Forms.Button button3; private System.Windows.Forms.TextBox tbIn; + private System.Windows.Forms.GroupBox panelBottom; + private System.Windows.Forms.GroupBox panelTop; private System.Windows.Forms.TextBox tbOut; + private System.Windows.Forms.WebBrowser svgOut; + private System.Windows.Forms.Panel panelButtons; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button3; private System.Windows.Forms.Button button1; } } \ No newline at end of file diff --git a/SvgDocTest/DocForm.cs b/SvgDocTest/DocForm.cs index 1a9a48a..74edf33 100644 --- a/SvgDocTest/DocForm.cs +++ b/SvgDocTest/DocForm.cs @@ -18,11 +18,6 @@ public partial class DocForm : Form { [STAThread] private static void Main() => Application.Run(new DocForm()); - private static void RefreshBrowserFrom(WebBrowser browser, string filename) { - browser.Navigate(new Uri(filename)); - browser.Refresh(WebBrowserRefreshOption.Completely); - } - private void Button1_Click(object sender, EventArgs e) { using (var dlg = new OpenFileDialog { AutoUpgradeEnabled = true, @@ -96,18 +91,18 @@ private void Button2_Click(object sender, EventArgs e) { root.AddChild(grp2); //output - + SvgFactory.ResetNamespaces(); string s = root.WriteSVGString(true); - + tbIn.Text = s; tbOut.Text = s; string tempFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "foo.svg"); using (var tw = new StreamWriter(tempFile, false)) tw.Write(s); - - svgOut.Navigate(new Uri(tempFile)); - svgOut.Refresh(WebBrowserRefreshOption.Completely); + panelTop.Text = $"Input: {tempFile}"; + svgOut.RefreshFrom(tempFile); + svgIn.RefreshFrom(tempFile); } private void Button3_Click(object sender, EventArgs e) { @@ -131,11 +126,12 @@ private void Button3_Click(object sender, EventArgs e) { _ = MessageBox.Show("Tests completed Ok"); } private void ProcessSvgFile(string svgFileName) { + panelTop.Text = $"Input: {svgFileName}"; tbIn.Text = svgFileName.LoadText(); - RefreshBrowserFrom(svgIn, svgFileName); tbOut.Text = SvgFactory.LoadFromXML(svgFileName.LoadXml(), null).WriteSVGString(true); File.WriteAllText(_tempFileName, tbOut.Text); - RefreshBrowserFrom(svgOut, _tempFileName); + svgIn.RefreshFrom(svgFileName); + svgOut.RefreshFrom(_tempFileName); } } } diff --git a/SvgDocTest/Extensions.cs b/SvgDocTest/Extensions.cs new file mode 100644 index 0000000..5e0d7d3 --- /dev/null +++ b/SvgDocTest/Extensions.cs @@ -0,0 +1,11 @@ +using System; +using System.Windows.Forms; + +namespace SvgDocTest { + public static class Extensions { + public static void RefreshFrom(this WebBrowser browser, string filename) { + browser.Navigate(new Uri(filename)); + browser.Refresh(WebBrowserRefreshOption.Completely); + } + } +} diff --git a/SvgDotNetCoreTest/SvgDotNetCoreTest.csproj b/SvgDotNetCoreTest/SvgDotNetCoreTest.csproj index 1d8245b..09773e8 100644 --- a/SvgDotNetCoreTest/SvgDotNetCoreTest.csproj +++ b/SvgDotNetCoreTest/SvgDotNetCoreTest.csproj @@ -2,7 +2,7 @@ Exe 12 - net8.0-windows + net8.0-windows;net9.0-windows diff --git a/SvgNet/Elements/SvgElement.cs b/SvgNet/Elements/SvgElement.cs index 7854e93..a630b4e 100644 --- a/SvgNet/Elements/SvgElement.cs +++ b/SvgNet/Elements/SvgElement.cs @@ -8,6 +8,8 @@ using System.Xml; +using SvgNet.Elements; + namespace SvgNet.Elements; /// /// The base class for SVG elements. It represents some part of an SVG document, either an element (rect, circle etc) or a text item. Duties include: @@ -30,9 +32,6 @@ namespace SvgNet.Elements; /// /// public class SvgElement { - public const string svgNamespaceURI = "http://www.w3.org/2000/svg"; - public const string xlinkNamespaceURI = "http://www.w3.org/1999/xlink"; - public SvgElement() => Id = GenerateNewId(); public SvgElement(string id) => Id = id; @@ -98,15 +97,18 @@ public virtual SvgElement AddChildren(params SvgElement[] ch) { /// /// public virtual void ReadXmlElement(XmlDocument doc, XmlElement el) { - foreach (XmlAttribute att in el.Attributes) { - // TODO: after namespaced attributes are supported in the writer code (WriteXmlElements) re-enable - // their reading. - // For now we'll skip namespaced attributes - if (att.Name == "xmlns" || att.Name.Contains(':')) - continue; - - this[att.Name] = att.Value; - } + foreach (XmlAttribute att in el.Attributes) + if (att.Name.StartsWith("xmlns", StringComparison.Ordinal)) +#if NET5_0_OR_GREATER + SvgFactory._namespaces.TryAdd(att.Name, att.Value); +#else + { + if (!SvgFactory._namespaces.ContainsKey(att.Name)) + SvgFactory._namespaces.Add(att.Name, att.Value); + } +#endif + else + this[att.Name] = att.Value; } /// @@ -140,8 +142,8 @@ public string WriteSVGString(bool compressAttributes, SizeF? bounds) { //write out our SVG tree to the new XmlDocument WriteXmlElements(doc, null); - doc.DocumentElement.SetAttribute("xmlns", svgNamespaceURI); - doc.DocumentElement.SetAttribute("xmlns:xlink", xlinkNamespaceURI); + foreach (KeyValuePair pair in SvgFactory._namespaces) + doc.DocumentElement.SetAttribute(pair.Key, pair.Value); string ents = string.Empty; if (compressAttributes) ents = SvgFactory.CompressXML(doc, doc.DocumentElement); @@ -167,7 +169,7 @@ public virtual void WriteXmlElements(XmlDocument doc, XmlElement parent) { foreach (string s in _atts.Keys) _ = _atts[s] switch { float singleValue => me.SetAttribute(s, doc.NamespaceURI, singleValue.ToString(CultureInfo.InvariantCulture)), double doubleValue => me.SetAttribute(s, doc.NamespaceURI, doubleValue.ToString(CultureInfo.InvariantCulture)), - _ => me.SetAttribute(s, doc.NamespaceURI, _atts[s].ToString()), + _ => SetAttribute(doc, me, s, _atts[s].ToString()), }; foreach (SvgElement el in Children) el.WriteXmlElements(doc, me); @@ -194,6 +196,16 @@ T SetNewAttributeValue(T st) { private static string GenerateNewId() => _idcounter++.ToString(); + protected static string SetAttribute(XmlDocument doc, XmlElement me, string name, string value) { + string[] parts = name.Split(':'); + return parts.Length switch { + 1 => me.SetAttribute(name, doc.NamespaceURI, value), + 2 => me.SetAttribute(parts[1], NamespaceForPrefix("xmlns:" + parts[0]), value), + _ => throw new InvalidOperationException($"Attribute name has more tha one ':' => '{name}'") + }; + } + + private static string NamespaceForPrefix(string xmlns) => SvgFactory._namespaces.TryGetValue(xmlns, out string namespaceURI) ? namespaceURI : string.Empty; private class DummyXmlResolver : XmlResolver { public override System.Net.ICredentials Credentials { set { } } diff --git a/SvgNet/Elements/SvgStyledElement.cs b/SvgNet/Elements/SvgStyledElement.cs index d1dc62a..c3cee60 100644 --- a/SvgNet/Elements/SvgStyledElement.cs +++ b/SvgNet/Elements/SvgStyledElement.cs @@ -44,9 +44,21 @@ public SvgTransformList Transform { /// /// public override void ReadXmlElement(XmlDocument doc, XmlElement el) { - foreach (XmlAttribute att in el.Attributes) if (att.Name == "style") Style = new SvgStyle(att.Value); - else if (att.Name == "transform") Transform = new SvgTransformList(att.Value); - else this[att.Name] = att.Value; + foreach (XmlAttribute att in el.Attributes) { + string name = att.Name; + string value = att.Value; + switch (name) { + case "style": + Style = new SvgStyle(value); + break; + case "transform": + Transform = new SvgTransformList(value); + break; + default: + this[name] = value; + break; + } + } } /// @@ -58,20 +70,25 @@ public override void ReadXmlElement(XmlDocument doc, XmlElement el) { /// public override void WriteXmlElements(XmlDocument doc, XmlElement parent) { XmlElement me = doc.CreateElement("", Name, doc.NamespaceURI); - foreach (string s in _atts.Keys) { - object attribute = _atts[s]; - if (attribute != null) if (s == "style") WriteStyle(doc, me, attribute); - else if (s == "transform") WriteTransform(doc, me, attribute); - else { - // xlink qualified attributes are an special case because they need an special namespace - (bool isPrefixed, string localName) = s.IsPrefixedBy("xlink:"); - _ = isPrefixed - ? me.SetAttribute(localName, xlinkNamespaceURI, _atts[s].ToString()) - : me.SetAttribute(s, doc.NamespaceURI, _atts[s].ToString()); - } + foreach (string name in _atts.Keys) { + object attribute = _atts[name]; + if (attribute is null) + continue; + switch (name) { + case "style": + WriteStyle(doc, me, attribute); + break; + case "transform": + WriteTransform(doc, me, attribute); + break; + default: + SetAttribute(doc, me, name, attribute.ToString()); + break; + } } - foreach (SvgElement el in Children) el.WriteXmlElements(doc, me); + foreach (SvgElement el in Children) + el.WriteXmlElements(doc, me); _ = parent == null ? doc.AppendChild(me) : parent.AppendChild(me); } diff --git a/SvgNet/Extensions/StringExtensions.cs b/SvgNet/Extensions/StringExtensions.cs index 0f640ea..0e460c5 100644 --- a/SvgNet/Extensions/StringExtensions.cs +++ b/SvgNet/Extensions/StringExtensions.cs @@ -16,17 +16,6 @@ public static int ParseHex(this string s, int startIndex, int length = 1) => int.Parse(s.Substring(startIndex, length), NumberStyles.HexNumber, CultureInfo.InvariantCulture); #endif - public static IPB IsPrefixedBy(this string s, string prefix) - => !s.StartsWith(prefix, StringComparison.InvariantCulture) - ? new IPB(false, null) - : new IPB(true, -#if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER - s[prefix.Length..] -#else - s.Substring(prefix.Length) -#endif - ); - public static string SkipFirst(this string s) #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER => s[1..]; @@ -34,9 +23,17 @@ public static string SkipFirst(this string s) => s.Substring(1); #endif +#if NET8_0_OR_GREATER + private static readonly Buffers.SearchValues digits = Buffers.SearchValues.Create(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']); +#else private static readonly char[] digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; +#endif public static bool TrySplitNumberAndSuffix(this string s, out string number, out string suffix) { +#if NET8_0_OR_GREATER + int i = s.AsSpan().LastIndexOfAny(digits) + 1; +#else int i = s.LastIndexOfAny(digits) + 1; +#endif suffix = number = null; if (i == 0) return false; @@ -68,14 +65,3 @@ public static bool TryParseTransformation(this string s, out string name, out fl return true; } } - -public readonly struct IPB(bool isPrefixed, string tail) { - public readonly bool IsPrefixed = isPrefixed; - public readonly string Tail = tail; - - public void Deconstruct(out bool isPrefixed, out string tail) { - isPrefixed = IsPrefixed; - tail = Tail; - } -} - diff --git a/SvgNet/SvgFactory.cs b/SvgNet/SvgFactory.cs index e939c08..816a933 100644 --- a/SvgNet/SvgFactory.cs +++ b/SvgNet/SvgFactory.cs @@ -16,6 +16,9 @@ namespace SvgNet; /// Static methods to produce/write/copy Svg documents reside in this class. /// public static class SvgFactory { + public const string svgNamespaceURI = "http://www.w3.org/2000/svg"; + public const string xlinkNamespaceURI = "http://www.w3.org/1999/xlink"; + /// /// Used by LoadFromXML /// @@ -56,6 +59,8 @@ public static SvgElement CloneElement(SvgElement el) { return clone; } + internal static Dictionary _namespaces = new() { ["xmlns"] = svgNamespaceURI, ["xmlns:xlink"] = xlinkNamespaceURI }; + /// /// Given an xml document and (optionally) a particular element to start from, read the xml nodes and construct /// a tree of objects. Xml tags that do not correspond to a particular class will be @@ -68,6 +73,7 @@ public static SvgElement CloneElement(SvgElement el) { /// /// public static SvgElement LoadFromXML(XmlDocument doc, XmlElement el) { + ResetNamespaces(); if (el == null) { foreach (XmlNode noddo in doc.ChildNodes) { if (noddo.GetType() == typeof(XmlElement)) { @@ -220,6 +226,9 @@ private static void RecLoadFromXML(SvgElement e, XmlDocument doc, XmlElement el) } } + public static void ResetNamespaces() => _namespaces = new() { ["xmlns"] = svgNamespaceURI, ["xmlns:xlink"] = xlinkNamespaceURI }; + + private struct EntitySingleton { public string AttributeName; public XmlElement Element; diff --git a/SvgNet/SvgNet.csproj b/SvgNet/SvgNet.csproj index dbe2e0e..811a10d 100644 --- a/SvgNet/SvgNet.csproj +++ b/SvgNet/SvgNet.csproj @@ -1,13 +1,13 @@  - netstandard2.0;netstandard2.1;net8.0 - $(TargetFrameworks);net462;net8.0-windows - true + netstandard2.0;netstandard2.1;net8.0;net9.0 + $(TargetFrameworks);net462;net8.0-windows;net9.0-windows + true preview SVG SvgNet - 3.4.0 + 3.5.0 SvgNet svgnetdoc.xml CS1591 diff --git a/SvgNetUnitTests/SvgNetUnitTests.csproj b/SvgNetUnitTests/SvgNetUnitTests.csproj index 285405c..305dd26 100644 --- a/SvgNetUnitTests/SvgNetUnitTests.csproj +++ b/SvgNetUnitTests/SvgNetUnitTests.csproj @@ -1,7 +1,7 @@  preview - net8.0 + net8.0;net9.0 SvgNet