diff --git a/OpenXmlFormats/Wordprocessing/Markup.cs b/OpenXmlFormats/Wordprocessing/Markup.cs index 0fe91cb4c..9fc2b7a3f 100644 --- a/OpenXmlFormats/Wordprocessing/Markup.cs +++ b/OpenXmlFormats/Wordprocessing/Markup.cs @@ -182,15 +182,49 @@ public class CT_Comments private List commentField; + private Dictionary extraXmlNamespace = new Dictionary(); + private Dictionary defaultXmlNamespace = new Dictionary(){ + { "o", "urn:schemas-microsoft-com:office:office" }, + { "ve", "http://schemas.openxmlformats.org/markup-compatibility/2006" }, + { "r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships" }, + { "v", "urn:schemas-microsoft-com:vml" }, + { "m", "http://schemas.openxmlformats.org/officeDocument/2006/math" }, + { "w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main" }, + { "wp", "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" }, + { "w10", "urn:schemas-microsoft-com:office:word" }, + { "w14", "http://schemas.microsoft.com/office/word/2010/wordml" }, + { "wne", "http://schemas.microsoft.com/office/word/2006/wordml" }, + + }; public CT_Comments() { this.commentField = new List(); } + private void FindExtraNamespace(XmlNode node) + { + if (node.Attributes != null) + { + foreach (XmlAttribute attr in node.Attributes) + { + if (attr.Name.StartsWith("xmlns:")) + { + string[] strs = attr.Name.Split(':'); + if (strs.Length == 2) + { + if (!extraXmlNamespace.ContainsKey(strs[1]) && + !defaultXmlNamespace.ContainsKey(strs[1])) + extraXmlNamespace.Add(strs[1], attr.Value); + } + } + } + } + } public static CT_Comments Parse(XmlNode node, XmlNamespaceManager namespaceManager) { if (node == null) return null; CT_Comments ctObj = new CT_Comments(); + ctObj.FindExtraNamespace(node); ctObj.comment = new List(); foreach (XmlNode childNode in node.ChildNodes) { @@ -201,16 +235,21 @@ public static CT_Comments Parse(XmlNode node, XmlNamespaceManager namespaceManag } - + private void WriteXmlNamespace(StreamWriter sw, Dictionary ns) + { + if(ns == null || ns.Count == 0) + return; + foreach (var kv in ns) + { + sw.Write(string.Format($"xmlns:{kv.Key}=\"{kv.Value}\" ", kv.Key, kv.Value)); + } + } internal void Write(StreamWriter sw) { sw.Write(""); sw.Write(string.Format(""); + WriteXmlNamespace(sw, defaultXmlNamespace); + WriteXmlNamespace(sw, extraXmlNamespace); sw.Write(">"); if (this.comment != null) { diff --git a/testcases/ooxml/XWPF/UserModel/TestXWPFComments.cs b/testcases/ooxml/XWPF/UserModel/TestXWPFComments.cs index 7bf13cd5c..7843a13b9 100644 --- a/testcases/ooxml/XWPF/UserModel/TestXWPFComments.cs +++ b/testcases/ooxml/XWPF/UserModel/TestXWPFComments.cs @@ -3,6 +3,8 @@ using NPOI.XWPF.UserModel; using NUnit.Framework; using System.Collections.Generic; + using System.IO; + using System.Reflection.Metadata; [TestFixture] public class TestXWPFComments @@ -56,5 +58,31 @@ public void TestReadComments() Assert.AreEqual(1, allPictures.Count); } } + + [Test] + public void TestNPOIBug1481() + { + using(XWPFDocument doc = XWPFTestDataSamples.OpenSampleDocument("NPOI-bug-1481.docx")) + { + XWPFComment[] comments = doc.GetComments(); + Assert.AreEqual(1, comments.Length); + + XWPFComment comment = comments[0]; + Assert.AreEqual("Claudio Pais", comment.GetAuthor()); + Assert.AreEqual("2025-01-23T17:18:00Z", comment.Date); + Assert.AreEqual("Bla bla", comment.GetText()); + + XWPFDocument docIn = XWPFTestDataSamples.WriteOutAndReadBack(doc); + + comments = docIn.GetComments(); + Assert.AreEqual(1, comments.Length); + + comment = comments[0]; + Assert.AreEqual("Claudio Pais", comment.GetAuthor()); + Assert.AreEqual("2025-01-23T17:18:00Z", comment.Date); + Assert.AreEqual("Bla bla", comment.GetText()); + + } + } } } diff --git a/testcases/test-data/document/NPOI-bug-1481.docx b/testcases/test-data/document/NPOI-bug-1481.docx new file mode 100644 index 000000000..1a603d943 Binary files /dev/null and b/testcases/test-data/document/NPOI-bug-1481.docx differ