From af57fa6d736a8c029e0d50d408de0f5d8c31460b Mon Sep 17 00:00:00 2001 From: WX Date: Sat, 7 Sep 2024 21:09:01 +0800 Subject: [PATCH 01/10] =?UTF-8?q?=E6=89=A9=E5=B1=95=EF=BC=9A=E8=A1=A8?= =?UTF-8?q?=E6=A0=BC=E6=94=AF=E6=8C=81Obj.objA.List.Prop1=E6=B8=B2?= =?UTF-8?q?=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MiniWord/MiniWord.Implment.cs | 91 ++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 9 deletions(-) diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs index cec059d..c5f430f 100644 --- a/src/MiniWord/MiniWord.Implment.cs +++ b/src/MiniWord/MiniWord.Implment.cs @@ -63,28 +63,55 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum .Cast().GroupBy(x => x.Value).Select(varGroup => varGroup.First().Value)).ToArray(); if (matchs.Length > 0) { - var listKeys = matchs.Select(s => s.Split('.')[0]).Distinct().ToArray(); + //var listKeys = matchs.Select(s => s.Split('.')[0]).Distinct().ToArray(); + //// TODO: + //// not support > 2 list in same tr + //if (listKeys.Length > 2) + // throw new NotSupportedException("MiniWord doesn't support more than 2 list in same row"); + //var listKey = listKeys[0]; + + var listLevelKeys = matchs.Select(s => s.Substring(0, s.LastIndexOf('.'))).Distinct().ToArray(); // TODO: // not support > 2 list in same tr - if (listKeys.Length > 2) + if (listLevelKeys.Length > 2) throw new NotSupportedException("MiniWord doesn't support more than 2 list in same row"); - var listKey = listKeys[0]; - if (tags.ContainsKey(listKey) && tags[listKey] is IEnumerable) + + var tagObj = GetObjVal(tags, listLevelKeys[0]); + + if (tagObj != null && tagObj is IEnumerable) { var attributeKey = matchs[0].Split('.')[0]; - var list = tags[listKey] as IEnumerable; + var list = tagObj as IEnumerable; - foreach (Dictionary es in list) + foreach (var item in list) { var dic = new Dictionary(); //TODO: optimize + var newTr = tr.CloneNode(true); - foreach (var e in es) + if (item is IDictionary) + { + var es = (Dictionary) item; + foreach (var e in es) + { + var dicKey = $"{listLevelKeys[0]}.{e.Key}"; + dic.Add(dicKey, e.Value); + } + } + // 支持Obj.A.B.C... + else { - var dicKey = $"{listKey}.{e.Key}"; - dic.Add(dicKey, e.Value); + var props = item.GetType().GetProperties(); + foreach (var p in props) + { + var dicKey = $"{listLevelKeys[0]}.{p.Name}"; + dic.Add(dicKey, p.GetValue(item)); + } } + + + ReplaceStatements(newTr, tags: dic); ReplaceText(newTr, docx, tags: dic); @@ -111,6 +138,52 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum ReplaceText(xmlElement, docx, tags); } + + /// + /// 获取Obj对象指定的值 + /// + /// 数据源 + /// 属性名,如“A.B” + /// + /// + private static object GetObjVal(object objSource, string propNames) + { + return GetObjVal(objSource, propNames.Split('.')); + } + + /// + /// 获取Obj对象指定的值 + /// + /// 数据源 + /// 属性名,如“A.B”即[0]A,[1]B + /// + /// + private static object GetObjVal(object objSource, string[] propNames) + { + var nextPropNames = propNames.Skip(1).ToArray(); + if (objSource is IDictionary) + { + var dict = (IDictionary)objSource; + if (dict.Contains(propNames[0])) + { + var val = dict[propNames[0]]; + if(propNames.Length >1) + return GetObjVal(dict[propNames[0]], nextPropNames); + else return val; + } + return null; + } + var prop1 = objSource.GetType().GetProperty(propNames[0]); + if (prop1 == null) + return null; + + var prop1Val = prop1.GetValue(objSource); + // 如果propNames只有一级,则直接返回对应的值 + if (propNames.Length == 1) + return prop1Val; + return GetObjVal(prop1Val, nextPropNames); + } + private static void AvoidSplitTagText(OpenXmlElement xmlElement) { var texts = xmlElement.Descendants().ToList(); From db4602fd0cdce134cd90c52492e673665acb5556 Mon Sep 17 00:00:00 2001 From: WX Date: Sat, 7 Sep 2024 21:12:50 +0800 Subject: [PATCH 02/10] =?UTF-8?q?=E6=89=A9=E5=B1=95=EF=BC=9A=E6=99=AE?= =?UTF-8?q?=E9=80=9A=E7=B1=BB=E5=9E=8B=E6=94=AF=E6=8C=81=E5=A4=9A=E7=BA=A7?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E6=B8=B2=E6=9F=93=EF=BC=8C=E5=A6=82{{Obj.A.B?= =?UTF-8?q?.C}}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MiniWord/MiniWord.Implment.cs | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs index c5f430f..4591833 100644 --- a/src/MiniWord/MiniWord.Implment.cs +++ b/src/MiniWord/MiniWord.Implment.cs @@ -1,4 +1,4 @@ -namespace MiniSoftware +namespace MiniSoftware { using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; @@ -423,9 +423,12 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen { foreach (var tag in tags) { - var isMatch = t.Text.Contains($"{{{{{tag.Key}}}}}"); + // 完全匹配 + var isFullMatch = t.Text.Contains($"{{{{{tag.Key}}}}}"); + // 层级匹配,如{{A.B.C.D}} + var partMatch = new Regex($".*{{{{({tag.Key}(\\.\\w+)+)}}}}.*").Match(t.Text); - if (!isMatch && tag.Value is List forTags) + if (!isFullMatch && tag.Value is List forTags) { if (forTags.Any(forTag => forTag.Value.Keys.Any(dictKey => { @@ -433,11 +436,11 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen return t.Text.Contains(innerTag); }))) { - isMatch = true; + isFullMatch = true; } } - if (isMatch) + if (isFullMatch || partMatch.Success) { if (tag.Value is string[] || tag.Value is IList || tag.Value is List) { @@ -447,6 +450,7 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen foreach (var v in vs) { var newT = t.CloneNode(true) as Text; + // todo newT.Text = t.Text.Replace($"{{{{{tag.Key}}}}}", v?.ToString()); if (isFirst) isFirst = false; @@ -537,19 +541,13 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen } else { - var newText = string.Empty; - if (tag.Value is DateTime) - { - newText = ((DateTime)tag.Value).ToString("yyyy-MM-dd HH:mm:ss"); - } - else - { - newText = tag.Value?.ToString(); - } - - t.Text = t.Text.Replace($"{{{{{tag.Key}}}}}", newText); + var k = isFullMatch ? tag.Key : partMatch.Groups[1].Value; + var v = isFullMatch ? tag.Value : GetObjVal(tags, k); + var newText = tag.Value is DateTime ? ((DateTime)v).ToString("yyyy-MM-dd HH:mm:ss") : v?.ToString(); + t.Text = t.Text.Replace($"{{{{{k}}}}}", newText); } } + } t.Text = EvaluateIfStatement(t.Text); From 9cbf6577fda370b9357ff7b8f6ac6541aa4b86fd Mon Sep 17 00:00:00 2001 From: WX Date: Sat, 7 Sep 2024 21:23:02 +0800 Subject: [PATCH 03/10] =?UTF-8?q?=E5=B5=8C=E5=A5=97=E5=AF=B9=E8=B1=A1MiniW?= =?UTF-8?q?ordPicture=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MiniWord/MiniWord.Implment.cs | 47 ++++++++++++++++--------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs index 4591833..2196e15 100644 --- a/src/MiniWord/MiniWord.Implment.cs +++ b/src/MiniWord/MiniWord.Implment.cs @@ -442,6 +442,9 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen if (isFullMatch || partMatch.Success) { + var key = isFullMatch ? tag.Key : partMatch.Groups[1].Value; + var value = isFullMatch ? tag.Value : GetObjVal(tags, key); + if (tag.Value is string[] || tag.Value is IList || tag.Value is List) { var vs = tag.Value as IEnumerable; @@ -498,9 +501,26 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen run.Append(generatedText); t.Remove(); } - else if (tag.Value is MiniWordPicture) + else if (IsHyperLink(tag.Value)) + { + AddHyperLink(docx, run, tag.Value); + t.Remove(); + } + else if (tag.Value is MiniWordColorText || tag.Value is MiniWordColorText[]) + { + if (tag.Value is MiniWordColorText) + { + AddColorText(run, new[] { (MiniWordColorText)tag.Value }); + } + else + { + AddColorText(run, (MiniWordColorText[])tag.Value); + } + t.Remove(); + } + else if (value is MiniWordPicture) { - var pic = (MiniWordPicture)tag.Value; + var pic = (MiniWordPicture)value; byte[] l_Data = null; if (pic.Path != null) { @@ -522,29 +542,10 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen } t.Remove(); } - else if (IsHyperLink(tag.Value)) - { - AddHyperLink(docx, run, tag.Value); - t.Remove(); - } - else if (tag.Value is MiniWordColorText || tag.Value is MiniWordColorText[]) - { - if (tag.Value is MiniWordColorText) - { - AddColorText(run, new[] { (MiniWordColorText)tag.Value }); - } - else - { - AddColorText(run, (MiniWordColorText[])tag.Value); - } - t.Remove(); - } else { - var k = isFullMatch ? tag.Key : partMatch.Groups[1].Value; - var v = isFullMatch ? tag.Value : GetObjVal(tags, k); - var newText = tag.Value is DateTime ? ((DateTime)v).ToString("yyyy-MM-dd HH:mm:ss") : v?.ToString(); - t.Text = t.Text.Replace($"{{{{{k}}}}}", newText); + var newText = tag.Value is DateTime ? ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss") : value?.ToString(); + t.Text = t.Text.Replace($"{{{{{key}}}}}", newText); } } From 41fc7d505a901d3d945ee9ee3927e0f9e6560db6 Mon Sep 17 00:00:00 2001 From: WX Date: Sat, 7 Sep 2024 21:38:12 +0800 Subject: [PATCH 04/10] =?UTF-8?q?=E5=85=B6=E4=BB=96=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E5=B5=8C=E5=A5=97=E5=AF=B9=E8=B1=A1=E6=B8=B2=E6=9F=93=EF=BC=88?= =?UTF-8?q?=E6=9C=AA=E5=AE=8C=E5=85=A8=E9=80=82=E9=85=8D=E3=80=81=E5=BE=85?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MiniWord/MiniWord.Implment.cs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs index 2196e15..a7917e6 100644 --- a/src/MiniWord/MiniWord.Implment.cs +++ b/src/MiniWord/MiniWord.Implment.cs @@ -445,16 +445,16 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen var key = isFullMatch ? tag.Key : partMatch.Groups[1].Value; var value = isFullMatch ? tag.Value : GetObjVal(tags, key); - if (tag.Value is string[] || tag.Value is IList || tag.Value is List) + if (value is string[] || value is IList || value is List) { - var vs = tag.Value as IEnumerable; + var vs = value as IEnumerable; var currentT = t; var isFirst = true; foreach (var v in vs) { var newT = t.CloneNode(true) as Text; // todo - newT.Text = t.Text.Replace($"{{{{{tag.Key}}}}}", v?.ToString()); + newT.Text = t.Text.Replace($"{{{{{key}}}}}", v?.ToString()); if (isFirst) isFirst = false; else @@ -465,7 +465,8 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen } t.Remove(); } - else if (tag.Value is List vs) + // todo 未验证嵌套对象的渲染 + else if (value is List vs) { var currentT = t; var generatedText = new Text(); @@ -478,6 +479,7 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen foreach (var vv in vs[i].Value) { + // todo tag,Key newT.Text = newT.Text.Replace("{{" + tag.Key + "." + vv.Key + "}}", vv.Value.ToString()); } @@ -501,20 +503,20 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen run.Append(generatedText); t.Remove(); } - else if (IsHyperLink(tag.Value)) + else if (IsHyperLink(value)) { - AddHyperLink(docx, run, tag.Value); + AddHyperLink(docx, run, value); t.Remove(); } - else if (tag.Value is MiniWordColorText || tag.Value is MiniWordColorText[]) + else if (value is MiniWordColorText || value is MiniWordColorText[]) { - if (tag.Value is MiniWordColorText) + if (value is MiniWordColorText) { - AddColorText(run, new[] { (MiniWordColorText)tag.Value }); + AddColorText(run, new[] { (MiniWordColorText)value }); } else { - AddColorText(run, (MiniWordColorText[])tag.Value); + AddColorText(run, (MiniWordColorText[])value); } t.Remove(); } @@ -544,7 +546,7 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen } else { - var newText = tag.Value is DateTime ? ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss") : value?.ToString(); + var newText = value is DateTime ? ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss") : value?.ToString(); t.Text = t.Text.Replace($"{{{{{key}}}}}", newText); } } From a319c800fed93c8964531689522929b7e4a947fb Mon Sep 17 00:00:00 2001 From: WX Date: Sun, 8 Sep 2024 16:40:51 +0800 Subject: [PATCH 05/10] =?UTF-8?q?@foreach=E5=BE=AA=E7=8E=AF=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类似@if Tag --- README.zh-CN.md | 33 ++ src/MiniWord/MiniWord.Implment.cs | 566 +++++++++++++++++++----------- 2 files changed, 398 insertions(+), 201 deletions(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index 22f68ec..f7f74d9 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -273,6 +273,39 @@ MiniWord.SaveAsByTemplate(path, templatePath, value); ![after_if](https://user-images.githubusercontent.com/38832863/220125435-72ea24b4-2412-45de-961a-ad4b2134417b.PNG) +### 循环 + + `@foreach` 和 `@endforeach` tags . + +##### Example + +```csharp +var value = new +{ + LoopData = new List() + { + new { + Type="类型A", + Items = new List() {new {Name = "A-1"}, new {Name = "A-2"},} + }, + new + { + Type="类型B", + Items = new List() {new {Name = "B-1"}, new {Name = "B-2"}, new {Name = "B-3"},} + }, + } +}; +MiniWord.SaveAsByTemplate(path, templatePath, value); +``` + +##### Template + +![1](https://github.com/user-attachments/assets/5d32241d-3977-46e7-b3de-cae130e5a653) + +##### Result + +![2](https://github.com/user-attachments/assets/69daa15e-4864-483e-b132-d8e867b6d1d1) + ### 多彩字体 ##### 代码例子 diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs index a7917e6..41b46b0 100644 --- a/src/MiniWord/MiniWord.Implment.cs +++ b/src/MiniWord/MiniWord.Implment.cs @@ -15,6 +15,8 @@ using A = DocumentFormat.OpenXml.Drawing; using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing; using PIC = DocumentFormat.OpenXml.Drawing.Pictures; + using System.Xml; + using System.Xml.Linq; public static partial class MiniWord { @@ -48,94 +50,122 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum // avoid {{tag}} like aa{{ test in... AvoidSplitTagText(xmlElement); + // @foreach循环体 + ReplaceForeachStatements(xmlElement,docx,tags); + //Tables - var tables = xmlElement.Descendants().ToArray(); + // 忽略table中没有占位符“{{}}”的表格 + var tables = xmlElement.Descendants
().Where(t => t.InnerText.Contains("{{")).ToArray(); { foreach (var table in tables) { - var trs = table.Descendants().ToArray(); // remember toarray or system will loop OOM; + GenerateTable(table,docx,tags); + } + } - foreach (var tr in trs) - { - var innerText = tr.InnerText.Replace("{{foreach", "").Replace("endforeach}}", "") - .Replace("{{if(", "").Replace(")if", "").Replace("endif}}", ""); - var matchs = (Regex.Matches(innerText, "(?<={{).*?\\..*?(?=}})") - .Cast().GroupBy(x => x.Value).Select(varGroup => varGroup.First().Value)).ToArray(); - if (matchs.Length > 0) - { - //var listKeys = matchs.Select(s => s.Split('.')[0]).Distinct().ToArray(); - //// TODO: - //// not support > 2 list in same tr - //if (listKeys.Length > 2) - // throw new NotSupportedException("MiniWord doesn't support more than 2 list in same row"); - //var listKey = listKeys[0]; - - var listLevelKeys = matchs.Select(s => s.Substring(0, s.LastIndexOf('.'))).Distinct().ToArray(); - // TODO: - // not support > 2 list in same tr - if (listLevelKeys.Length > 2) - throw new NotSupportedException("MiniWord doesn't support more than 2 list in same row"); - - var tagObj = GetObjVal(tags, listLevelKeys[0]); - - if (tagObj != null && tagObj is IEnumerable) - { - var attributeKey = matchs[0].Split('.')[0]; - var list = tagObj as IEnumerable; + ReplaceIfStatements(xmlElement, tags); - foreach (var item in list) - { - var dic = new Dictionary(); //TODO: optimize + ReplaceText(xmlElement, docx, tags); + } + /// + /// 渲染Table + /// + /// + /// + /// + /// + private static void GenerateTable(Table table, WordprocessingDocument docx, Dictionary tags) + { + var trs = table.Descendants().ToArray(); // remember toarray or system will loop OOM; - var newTr = tr.CloneNode(true); - if (item is IDictionary) - { - var es = (Dictionary) item; - foreach (var e in es) - { - var dicKey = $"{listLevelKeys[0]}.{e.Key}"; - dic.Add(dicKey, e.Value); - } - } - // 支持Obj.A.B.C... - else - { - var props = item.GetType().GetProperties(); - foreach (var p in props) - { - var dicKey = $"{listLevelKeys[0]}.{p.Name}"; - dic.Add(dicKey, p.GetValue(item)); - } - } + foreach (var tr in trs) + { + var innerText = tr.InnerText.Replace("{{foreach", "").Replace("endforeach}}", "") + .Replace("{{if(", "").Replace(")if", "").Replace("endif}}", ""); + // 匹配list数据,格式“Items.PropName” + var matchs = (Regex.Matches(innerText, "(?<={{).*?\\..*?(?=}})") + .Cast().GroupBy(x => x.Value).Select(varGroup => varGroup.First().Value)).ToArray(); + if (matchs.Length > 0) + { + //var listKeys = matchs.Select(s => s.Split('.')[0]).Distinct().ToArray(); + //// TODO: + //// not support > 2 list in same tr + //if (listKeys.Length > 2) + // throw new NotSupportedException("MiniWord doesn't support more than 2 list in same row"); + //var listKey = listKeys[0]; - + var listLevelKeys = matchs.Select(s => s.Substring(0, s.LastIndexOf('.'))).Distinct().ToArray(); + // TODO: + // not support > 2 list in same tr + if (listLevelKeys.Length > 2) + throw new NotSupportedException("MiniWord doesn't support more than 2 list in same row"); - ReplaceStatements(newTr, tags: dic); + var tagObj = GetObjVal(tags, listLevelKeys[0]); - ReplaceText(newTr, docx, tags: dic); - //Fix #47 The table should be inserted at the template tag position instead of the last row - if (table.Contains(tr)) - { - table.InsertBefore(newTr, tr); - } - else - { - // If it is a nested table, temporarily append it to the end according to the original plan. - table.Append(newTr); - } + if(tagObj == null) continue; + + if (tagObj is IEnumerable) + { + var attributeKey = matchs[0].Split('.')[0]; + var list = tagObj as IEnumerable; + + foreach (var item in list) + { + var dic = new Dictionary(); //TODO: optimize + + + var newTr = tr.CloneNode(true); + if (item is IDictionary) + { + var es = (Dictionary)item; + foreach (var e in es) + { + var dicKey = $"{listLevelKeys[0]}.{e.Key}"; + dic.Add(dicKey, e.Value); } - tr.Remove(); + } + // 支持Obj.A.B.C... + else + { + var props = item.GetType().GetProperties(); + foreach (var p in props) + { + var dicKey = $"{listLevelKeys[0]}.{p.Name}"; + dic.Add(dicKey, p.GetValue(item)); + } + } + + ReplaceIfStatements(newTr, tags: dic); + + ReplaceText(newTr, docx, tags: dic); + //Fix #47 The table should be inserted at the template tag position instead of the last row + if (table.Contains(tr)) + { + table.InsertBefore(newTr, tr); + } + else + { + // If it is a nested table, temporarily append it to the end according to the original plan. + table.Append(newTr); } } + tr.Remove(); + } + else + { + } } - } - - ReplaceStatements(xmlElement, tags); + else + { + var matchTxtProp = new Regex(@"(?<={{).*?\.?.*?(?=}})").Match(innerText); + if(!matchTxtProp.Success) return; - ReplaceText(xmlElement, docx, tags); + ReplaceText(tr, docx, tags); + } + } } @@ -173,6 +203,7 @@ private static object GetObjVal(object objSource, string[] propNames) } return null; } + // todo objSource = list var prop1 = objSource.GetType().GetProperty(propNames[0]); if (prop1 == null) return null; @@ -407,182 +438,315 @@ private static object EvaluateValue(string value) return value; } - private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocument docx, Dictionary tags) + /// + /// 替换单个paragraph属性值 + /// + /// + /// + /// + private static void ReplaceText(Paragraph p, WordprocessingDocument docx, Dictionary tags) { - var paragraphs = xmlElement.Descendants().ToArray(); - foreach (var p in paragraphs) - { - var runs = p.Descendants().ToArray(); + var runs = p.Descendants().ToArray(); - foreach (var run in runs) + foreach (var run in runs) + { + var texts = run.Descendants().ToArray(); + if (texts.Length == 0) + continue; + foreach (Text t in texts) { - var texts = run.Descendants().ToArray(); - if (texts.Length == 0) - continue; - foreach (Text t in texts) + foreach (var tag in tags) { - foreach (var tag in tags) + // 完全匹配 + var isFullMatch = t.Text.Contains($"{{{{{tag.Key}}}}}"); + // 层级匹配,如{{A.B.C.D}} + var partMatch = new Regex($".*{{{{({tag.Key}(\\.\\w+)+)}}}}.*").Match(t.Text); + + if (!isFullMatch && tag.Value is List forTags) { - // 完全匹配 - var isFullMatch = t.Text.Contains($"{{{{{tag.Key}}}}}"); - // 层级匹配,如{{A.B.C.D}} - var partMatch = new Regex($".*{{{{({tag.Key}(\\.\\w+)+)}}}}.*").Match(t.Text); + if (forTags.Any(forTag => forTag.Value.Keys.Any(dictKey => + { + var innerTag = "{{" + tag.Key + "." + dictKey + "}}"; + return t.Text.Contains(innerTag); + }))) + { + isFullMatch = true; + } + } - if (!isFullMatch && tag.Value is List forTags) + if (isFullMatch || partMatch.Success) + { + var key = isFullMatch ? tag.Key : partMatch.Groups[1].Value; + var value = isFullMatch ? tag.Value : GetObjVal(tags, key); + + if (value is string[] || value is IList || value is List) { - if (forTags.Any(forTag => forTag.Value.Keys.Any(dictKey => - { - var innerTag = "{{" + tag.Key + "." + dictKey + "}}"; - return t.Text.Contains(innerTag); - }))) + var vs = value as IEnumerable; + var currentT = t; + var isFirst = true; + foreach (var v in vs) { - isFullMatch = true; + var newT = t.CloneNode(true) as Text; + // todo + newT.Text = t.Text.Replace($"{{{{{key}}}}}", v?.ToString()); + if (isFirst) + isFirst = false; + else + run.Append(new Break()); + newT.Text = EvaluateIfStatement(newT.Text); + run.Append(newT); + currentT = newT; } + t.Remove(); } - - if (isFullMatch || partMatch.Success) + // todo 未验证嵌套对象的渲染 + else if (value is List vs) { - var key = isFullMatch ? tag.Key : partMatch.Groups[1].Value; - var value = isFullMatch ? tag.Value : GetObjVal(tags, key); + var currentT = t; + var generatedText = new Text(); + currentT.Text = currentT.Text.Replace(@"{{foreach", "").Replace(@"endforeach}}", ""); - if (value is string[] || value is IList || value is List) + var newTexts = new Dictionary(); + for (var i = 0; i < vs.Count; i++) { - var vs = value as IEnumerable; - var currentT = t; - var isFirst = true; - foreach (var v in vs) - { - var newT = t.CloneNode(true) as Text; - // todo - newT.Text = t.Text.Replace($"{{{{{key}}}}}", v?.ToString()); - if (isFirst) - isFirst = false; - else - run.Append(new Break()); - newT.Text = EvaluateIfStatement(newT.Text); - run.Append(newT); - currentT = newT; - } - t.Remove(); - } - // todo 未验证嵌套对象的渲染 - else if (value is List vs) - { - var currentT = t; - var generatedText = new Text(); - currentT.Text = currentT.Text.Replace(@"{{foreach", "").Replace(@"endforeach}}", ""); + var newT = t.CloneNode(true) as Text; - var newTexts = new Dictionary(); - for (var i = 0; i < vs.Count; i++) + foreach (var vv in vs[i].Value) { - var newT = t.CloneNode(true) as Text; + // todo tag,Key + newT.Text = newT.Text.Replace("{{" + tag.Key + "." + vv.Key + "}}", vv.Value.ToString()); + } - foreach (var vv in vs[i].Value) - { - // todo tag,Key - newT.Text = newT.Text.Replace("{{" + tag.Key + "." + vv.Key + "}}", vv.Value.ToString()); - } + newT.Text = EvaluateIfStatement(newT.Text); - newT.Text = EvaluateIfStatement(newT.Text); + if (!string.IsNullOrEmpty(newT.Text)) + newTexts.Add(i, newT.Text); + } - if (!string.IsNullOrEmpty(newT.Text)) - newTexts.Add(i, newT.Text); - } + for (var i = 0; i < newTexts.Count; i++) + { + var dict = newTexts.ElementAt(i); + generatedText.Text += dict.Value; - for (var i = 0; i < newTexts.Count; i++) + if (i != newTexts.Count - 1) { - var dict = newTexts.ElementAt(i); - generatedText.Text += dict.Value; - - if (i != newTexts.Count - 1) - { - generatedText.Text += vs[dict.Key].Separator; - } + generatedText.Text += vs[dict.Key].Separator; } + } - run.Append(generatedText); - t.Remove(); + run.Append(generatedText); + t.Remove(); + } + else if (IsHyperLink(value)) + { + AddHyperLink(docx, run, value); + t.Remove(); + } + else if (value is MiniWordColorText || value is MiniWordColorText[]) + { + if (value is MiniWordColorText) + { + AddColorText(run, new[] { (MiniWordColorText)value }); } - else if (IsHyperLink(value)) + else { - AddHyperLink(docx, run, value); - t.Remove(); + AddColorText(run, (MiniWordColorText[])value); } - else if (value is MiniWordColorText || value is MiniWordColorText[]) + t.Remove(); + } + else if (value is MiniWordPicture) + { + var pic = (MiniWordPicture)value; + byte[] l_Data = null; + if (pic.Path != null) { - if (value is MiniWordColorText) - { - AddColorText(run, new[] { (MiniWordColorText)value }); - } - else - { - AddColorText(run, (MiniWordColorText[])value); - } - t.Remove(); + l_Data = File.ReadAllBytes(pic.Path); } - else if (value is MiniWordPicture) + if (pic.Bytes != null) { - var pic = (MiniWordPicture)value; - byte[] l_Data = null; - if (pic.Path != null) - { - l_Data = File.ReadAllBytes(pic.Path); - } - if (pic.Bytes != null) - { - l_Data = pic.Bytes; - } - - var mainPart = docx.MainDocumentPart; + l_Data = pic.Bytes; + } - var imagePart = mainPart.AddImagePart(pic.GetImagePartType); - using (var stream = new MemoryStream(l_Data)) - { - imagePart.FeedData(stream); - AddPicture(run, mainPart.GetIdOfPart(imagePart), pic); + var mainPart = docx.MainDocumentPart; - } - t.Remove(); - } - else + var imagePart = mainPart.AddImagePart(pic.GetImagePartType); + using (var stream = new MemoryStream(l_Data)) { - var newText = value is DateTime ? ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss") : value?.ToString(); - t.Text = t.Text.Replace($"{{{{{key}}}}}", newText); + imagePart.FeedData(stream); + AddPicture(run, mainPart.GetIdOfPart(imagePart), pic); + } + t.Remove(); + } + else + { + var newText = value is DateTime ? ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss") : value?.ToString(); + t.Text = t.Text.Replace($"{{{{{key}}}}}", newText); } - } - t.Text = EvaluateIfStatement(t.Text); + } + + t.Text = EvaluateIfStatement(t.Text); - // add breakline + // add breakline + { + var newText = t.Text; + var splits = Regex.Split(newText, "(<[a-zA-Z/].*?>|\n|\r\n)").Where(o => o != "\n" && o != "\r\n"); + var currentT = t; + var isFirst = true; + if (splits.Count() > 1) { - var newText = t.Text; - var splits = Regex.Split(newText, "(<[a-zA-Z/].*?>|\n|\r\n)").Where(o => o != "\n" && o != "\r\n"); - var currentT = t; - var isFirst = true; - if (splits.Count() > 1) + foreach (var v in splits) { - foreach (var v in splits) - { - var newT = t.CloneNode(true) as Text; - newT.Text = v?.ToString(); - if (isFirst) - isFirst = false; - else - run.Append(new Break()); - run.Append(newT); - currentT = newT; - } - t.Remove(); + var newT = t.CloneNode(true) as Text; + newT.Text = v?.ToString(); + if (isFirst) + isFirst = false; + else + run.Append(new Break()); + run.Append(newT); + currentT = newT; } + t.Remove(); } } } } } - private static void ReplaceStatements(OpenXmlElement xmlElement, Dictionary tags) + private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocument docx, Dictionary tags) + { + var paragraphs = xmlElement.Descendants().ToArray(); + foreach (var p in paragraphs) + { + ReplaceText(p,docx,tags); + } + } + + /// + /// @foreach元素复制及填充 + /// + /// + private static void ReplaceForeachStatements(OpenXmlElement xmlElement,WordprocessingDocument docx,Dictionary data) + { + // 1. 先获取Foreach的元素 + var beginKey = "@foreach"; + var endKey = "@endforeach"; + var betweenEles = GetBetweenElements(xmlElement, beginKey, endKey, false); + if(betweenEles?.Any() != true) return; + + var beginParagraph = + xmlElement.Descendants().FirstOrDefault(p => p.InnerText.Contains(beginKey)); + var endParagraph = + xmlElement.Descendants().FirstOrDefault(p => p.InnerText.Contains(endKey)); + // 获取需循环的数据key + var match = new Regex(@".*{{(\w+(\.\w+)*)}}.*").Match(beginParagraph.InnerText); + if(!match.Success) throw new Exception($"@Foreach循环未找到对应数据"); + var foreachDataKey = match.Groups[1].Value; + + // 删除关键字文本行 + beginParagraph?.Remove(); + endParagraph?.Remove(); + // 循环体最后一个元素,用于新元素插入定位 + var lastEleInLoop = betweenEles.LastOrDefault(); + var copyLoopEles = betweenEles.Select(e => e.CloneNode(true)).ToList(); + // 需要循环的数据 + var foreachList = GetObjVal(data, foreachDataKey); + if (foreachList is IList list) + { + var loopEles = new List(); + for (var i = 0; i < list.Count; i++) + { + var item = list[i]; + var foreachDataDict = item.Obj2Dictionary(); + // 2. 渲染替换属性值{{}},插入循环元素,再替换…… + // 2.1 替换属性值 + if (i == 0) + loopEles = new List(betweenEles); + foreach (var ele in loopEles) + { + if (ele is Table table) + GenerateTable(table, docx, foreachDataDict); + else if (ele is Paragraph p) + { + ReplaceText(p, docx, foreachDataDict); + } + } + // 2.2 新增一个循环体元素 + if(list.Count - 1 > i) + { + loopEles.Clear(); + foreach (var ele in copyLoopEles) + { + var newEle = ele.CloneNode(true); + xmlElement.InsertAfter(newEle, lastEleInLoop); + lastEleInLoop = newEle; + loopEles.Add(newEle); + } + } + } + } + } + + /// + /// 将实体对象转为字典格式 + /// + /// + /// + private static Dictionary Obj2Dictionary(this object obj) + { + var result = new Dictionary(); + if(obj == null) return null; + if (obj is IDictionary d) + { + foreach (object key in d.Keys) + { + result.Add(key.ToString(), d[key]); + } + } + else + { + var props = obj.GetType().GetProperties(); + foreach (var p in props) + { + result.Add(p.Name,p.GetValue(obj)); + } + } + return result; + } + + /// + /// 获取关键词之间的元素 + /// + /// + /// + /// + /// 是否克隆元素对象。true:返回克隆元素;false:返回原始元素 + /// + private static List GetBetweenElements(OpenXmlElement sourceElement,string beginKey,string endKey,bool isCopyEle = true) + { + var beginParagraph = + sourceElement.Descendants().FirstOrDefault(p => p.InnerText.Contains(beginKey)); + var beginIndex = sourceElement.Elements().ToList().IndexOf(beginParagraph); + if (beginIndex < 0) return null; + + var result = new List(); + foreach (var element in sourceElement.Elements().Skip(beginIndex + 1)) + { + result.Add(isCopyEle ? element.CloneNode(true) : element); + if (element is Paragraph p && p.InnerText.Contains(endKey)) + { + // 移除endKey的paragraph + result.RemoveAt(result.Count - 1); + return result; + } + } + return result; + } + + + private static void ReplaceIfStatements(OpenXmlElement xmlElement, Dictionary tags) { var descendants = xmlElement.Descendants().ToList(); var paragraphs = xmlElement.Descendants().ToList(); From f2c5b630702d57ba9c0be4c9a2d2c28f9abb9625 Mon Sep 17 00:00:00 2001 From: WX Date: Mon, 9 Sep 2024 16:19:12 +0800 Subject: [PATCH 06/10] =?UTF-8?q?@foreach=E4=BB=A3=E7=A0=81=E5=9D=97?= =?UTF-8?q?=E4=B8=AD=E7=9A=84@if=E6=9D=A1=E4=BB=B6=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MiniWord/MiniWord.Implment.cs | 148 ++++++++++++++++++------------ 1 file changed, 87 insertions(+), 61 deletions(-) diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs index 41b46b0..18cef99 100644 --- a/src/MiniWord/MiniWord.Implment.cs +++ b/src/MiniWord/MiniWord.Implment.cs @@ -17,6 +17,7 @@ using PIC = DocumentFormat.OpenXml.Drawing.Pictures; using System.Xml; using System.Xml.Linq; + using DocumentFormat.OpenXml.Drawing.Charts; public static partial class MiniWord { @@ -633,60 +634,69 @@ private static void ReplaceForeachStatements(OpenXmlElement xmlElement,Wordproce // 1. 先获取Foreach的元素 var beginKey = "@foreach"; var endKey = "@endforeach"; - var betweenEles = GetBetweenElements(xmlElement, beginKey, endKey, false); - if(betweenEles?.Any() != true) return; - var beginParagraph = - xmlElement.Descendants().FirstOrDefault(p => p.InnerText.Contains(beginKey)); - var endParagraph = - xmlElement.Descendants().FirstOrDefault(p => p.InnerText.Contains(endKey)); - // 获取需循环的数据key - var match = new Regex(@".*{{(\w+(\.\w+)*)}}.*").Match(beginParagraph.InnerText); - if(!match.Success) throw new Exception($"@Foreach循环未找到对应数据"); - var foreachDataKey = match.Groups[1].Value; - - // 删除关键字文本行 - beginParagraph?.Remove(); - endParagraph?.Remove(); - // 循环体最后一个元素,用于新元素插入定位 - var lastEleInLoop = betweenEles.LastOrDefault(); - var copyLoopEles = betweenEles.Select(e => e.CloneNode(true)).ToList(); - // 需要循环的数据 - var foreachList = GetObjVal(data, foreachDataKey); - if (foreachList is IList list) + var betweenEles = GetBetweenElements(xmlElement, beginKey, endKey, false); + while (betweenEles?.Any() == true) { - var loopEles = new List(); - for (var i = 0; i < list.Count; i++) + var beginParagraph = + xmlElement.Descendants().FirstOrDefault(p => p.InnerText.Contains(beginKey)); + var endParagraph = + xmlElement.Descendants().FirstOrDefault(p => p.InnerText.Contains(endKey)); + // 获取需循环的数据key + var match = new Regex(@".*{{(\w+(\.\w+)*)}}.*").Match(beginParagraph.InnerText); + if (!match.Success) throw new Exception($"@Foreach循环未找到对应数据"); + var foreachDataKey = match.Groups[1].Value; + + // 删除关键字文本行 + beginParagraph?.Remove(); + endParagraph?.Remove(); + // 循环体最后一个元素,用于新元素插入定位 + var lastEleInLoop = betweenEles.LastOrDefault(); + var copyLoopEles = betweenEles.Select(e => e.CloneNode(true)).ToList(); + // 需要循环的数据 + var foreachList = GetObjVal(data, foreachDataKey); + if (foreachList is IList list) { - var item = list[i]; - var foreachDataDict = item.Obj2Dictionary(); - // 2. 渲染替换属性值{{}},插入循环元素,再替换…… - // 2.1 替换属性值 - if (i == 0) - loopEles = new List(betweenEles); - foreach (var ele in loopEles) + var loopEles = new List(); + for (var i = 0; i < list.Count; i++) { - if (ele is Table table) - GenerateTable(table, docx, foreachDataDict); - else if (ele is Paragraph p) + var item = list[i]; + var foreachDataDict = item.Obj2Dictionary(); + // 2. 渲染替换属性值{{}},插入循环元素,再替换…… + // 2.1 替换属性值 + if (i == 0) + loopEles = new List(betweenEles); + foreach (var ele in loopEles) { - ReplaceText(p, docx, foreachDataDict); + if (ele is Table table) + GenerateTable(table, docx, foreachDataDict); + else if (ele is Paragraph p) + { + ReplaceText(p, docx, foreachDataDict); + } } - } - // 2.2 新增一个循环体元素 - if(list.Count - 1 > i) - { - loopEles.Clear(); - foreach (var ele in copyLoopEles) + // @if代码块替换 + ReplaceIfStatements(xmlElement, loopEles, foreachDataDict); + + // 2.2 新增一个循环体元素 + if (list.Count - 1 > i) { - var newEle = ele.CloneNode(true); - xmlElement.InsertAfter(newEle, lastEleInLoop); - lastEleInLoop = newEle; - loopEles.Add(newEle); + loopEles.Clear(); + foreach (var ele in copyLoopEles) + { + var newEle = ele.CloneNode(true); + xmlElement.InsertAfter(newEle, lastEleInLoop); + lastEleInLoop = newEle; + loopEles.Add(newEle); + } } } } + + betweenEles = GetBetweenElements(xmlElement, beginKey, endKey, false); } + + } /// @@ -745,42 +755,58 @@ private static List GetBetweenElements(OpenXmlElement sourceElem return result; } - - private static void ReplaceIfStatements(OpenXmlElement xmlElement, Dictionary tags) + /// + /// @if处理逻辑 + /// + /// 根元素 + /// 包含@if-@end的元素集合 + /// + private static void ReplaceIfStatements(OpenXmlElement rootXmlElement, List elementList, Dictionary tags) { - var descendants = xmlElement.Descendants().ToList(); - var paragraphs = xmlElement.Descendants().ToList(); - + var paragraphs = elementList.Where(e=>e is Paragraph).ToList(); while (paragraphs.Any(s => s.InnerText.Contains("@if"))) { - var ifIndex = paragraphs.FindIndex(0, s => s.InnerText.Contains("@if")); - var endIfFinalIndex = paragraphs.FindIndex(ifIndex, s => s.InnerText.Contains("@endif")); + var ifP = paragraphs.First( s => s.InnerText.Contains("@if")); + var endIfP = paragraphs.First( s => s.InnerText.Contains("@endif")); - var statement = paragraphs[ifIndex].InnerText.Split(' '); + var statement = ifP.InnerText.Split(' '); - var tagValue = tags[statement[1]] ?? "NULL"; + //var tagValue = tags[statement[1]] ?? "NULL"; + var tagValue1 = GetObjVal(tags, statement[1]) ?? "NULL"; + var tagValue2 = GetObjVal(tags, statement[3]) ?? statement[3]; - var checkStatement = statement.Length == 4 ? EvaluateStatement(tagValue.ToString(), statement[2], statement[3]) : !bool.Parse(tagValue.ToString()); + var checkStatement = statement.Length == 4 ? EvaluateStatement(tagValue1.ToString(), statement[2], tagValue2.ToString()) : !bool.Parse(tagValue1.ToString()); if (!checkStatement) { - var paragraphIfIndex = descendants.FindIndex(a => a == paragraphs[ifIndex]); - var paragraphEndIfIndex = descendants.FindIndex(a => a == paragraphs[endIfFinalIndex]); + var paragraphIfIndex = elementList.FindIndex(a => a == ifP); + var paragraphEndIfIndex = elementList.FindIndex(a => a == endIfP); for (int i = paragraphIfIndex + 1; i <= paragraphEndIfIndex - 1; i++) { - descendants[i].Remove(); + rootXmlElement.RemoveChild(elementList[i]); } - } - paragraphs[ifIndex].Remove(); - paragraphs[endIfFinalIndex].Remove(); - - paragraphs = xmlElement.Descendants().ToList(); + rootXmlElement.RemoveChild(ifP); + rootXmlElement.RemoveChild(endIfP); + paragraphs.Remove(ifP); + paragraphs.Remove(endIfP); } } + /// + /// @if处理逻辑 + /// + /// @if-endif的父元素 + /// + private static void ReplaceIfStatements(OpenXmlElement xmlElement, Dictionary tags) + { + var descendants = xmlElement.Descendants().ToList(); + + ReplaceIfStatements(xmlElement,descendants, tags); + } + private static string EvaluateIfStatement(string text) { const string ifStartTag = "{{if("; From 9cd53a8f7bebec3181af2dbca91287ebbebfcee3 Mon Sep 17 00:00:00 2001 From: WX Date: Fri, 13 Sep 2024 11:52:04 +0800 Subject: [PATCH 07/10] =?UTF-8?q?FIx=EF=BC=9Atable=E6=B8=B2=E6=9F=93bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MiniWord/MiniWord.Implment.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs index 18cef99..50bd03a 100644 --- a/src/MiniWord/MiniWord.Implment.cs +++ b/src/MiniWord/MiniWord.Implment.cs @@ -124,7 +124,7 @@ private static void GenerateTable(Table table, WordprocessingDocument docx, Dict foreach (var e in es) { var dicKey = $"{listLevelKeys[0]}.{e.Key}"; - dic.Add(dicKey, e.Value); + dic[dicKey] = e.Value; } } // 支持Obj.A.B.C... @@ -134,7 +134,7 @@ private static void GenerateTable(Table table, WordprocessingDocument docx, Dict foreach (var p in props) { var dicKey = $"{listLevelKeys[0]}.{p.Name}"; - dic.Add(dicKey, p.GetValue(item)); + dic[dicKey] = p.GetValue(item); } } @@ -156,13 +156,24 @@ private static void GenerateTable(Table table, WordprocessingDocument docx, Dict } else { - + var dic = new Dictionary(); //TODO: optimize + + var props = tagObj.GetType().GetProperties(); + foreach (var p in props) + { + var dicKey = $"{listLevelKeys[0]}.{p.Name}"; + dic[dicKey] = p.GetValue(tagObj); + } + + ReplaceIfStatements(tr, tags: tagObj.ToDictionary()); + + ReplaceText(tr, docx, tags: dic); } } else { var matchTxtProp = new Regex(@"(?<={{).*?\.?.*?(?=}})").Match(innerText); - if(!matchTxtProp.Success) return; + if(!matchTxtProp.Success) continue; ReplaceText(tr, docx, tags); } From f4c2ea7dd95621b27d7536ea6304ef8c679464b0 Mon Sep 17 00:00:00 2001 From: WX Date: Mon, 23 Sep 2024 13:51:12 +0800 Subject: [PATCH 08/10] =?UTF-8?q?fix=EF=BC=9Aremove=E4=B9=8B=E5=89=8D?= =?UTF-8?q?=E5=85=88=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MiniWord/MiniWord.Implment.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs index 50bd03a..a3ac66b 100644 --- a/src/MiniWord/MiniWord.Implment.cs +++ b/src/MiniWord/MiniWord.Implment.cs @@ -795,12 +795,13 @@ private static void ReplaceIfStatements(OpenXmlElement rootXmlElement, Listc == elementList[i])) rootXmlElement.RemoveChild(elementList[i]); } } - - rootXmlElement.RemoveChild(ifP); - rootXmlElement.RemoveChild(endIfP); + if(rootXmlElement.ChildElements.Any(c => c == ifP)) + rootXmlElement.RemoveChild(ifP); + if (rootXmlElement.ChildElements.Any(c => c == endIfP)) + rootXmlElement.RemoveChild(endIfP); paragraphs.Remove(ifP); paragraphs.Remove(endIfP); } From 5b6be1c0049f8cae0fb191baf5e20ea49c63d35f Mon Sep 17 00:00:00 2001 From: WX Date: Mon, 23 Sep 2024 13:53:10 +0800 Subject: [PATCH 09/10] =?UTF-8?q?remove=EF=BC=9A=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E6=96=B9=E6=B3=95Obj2Dictionary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MiniWord/MiniWord.Implment.cs | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs index a3ac66b..75e540d 100644 --- a/src/MiniWord/MiniWord.Implment.cs +++ b/src/MiniWord/MiniWord.Implment.cs @@ -672,7 +672,7 @@ private static void ReplaceForeachStatements(OpenXmlElement xmlElement,Wordproce for (var i = 0; i < list.Count; i++) { var item = list[i]; - var foreachDataDict = item.Obj2Dictionary(); + var foreachDataDict = item.ToDictionary(); // 2. 渲染替换属性值{{}},插入循环元素,再替换…… // 2.1 替换属性值 if (i == 0) @@ -709,34 +709,7 @@ private static void ReplaceForeachStatements(OpenXmlElement xmlElement,Wordproce } - - /// - /// 将实体对象转为字典格式 - /// - /// - /// - private static Dictionary Obj2Dictionary(this object obj) - { - var result = new Dictionary(); - if(obj == null) return null; - if (obj is IDictionary d) - { - foreach (object key in d.Keys) - { - result.Add(key.ToString(), d[key]); - } - } - else - { - var props = obj.GetType().GetProperties(); - foreach (var p in props) - { - result.Add(p.Name,p.GetValue(obj)); - } - } - return result; - } - + /// /// 获取关键词之间的元素 /// From fd00b323b46ed14bf40bc1de57ec5271d2dfa2af Mon Sep 17 00:00:00 2001 From: WX Date: Fri, 27 Sep 2024 21:23:42 +0800 Subject: [PATCH 10/10] =?UTF-8?q?FIX=EF=BC=9A=E6=B2=A1=E6=9C=89=E5=BE=AA?= =?UTF-8?q?=E7=8E=AF=E6=95=B0=E6=8D=AE=E6=97=B6=EF=BC=8C=E5=88=A0=E9=99=A4?= =?UTF-8?q?@foreach-@endforeach=E4=B9=8B=E9=97=B4=E7=9A=84=E5=85=83?= =?UTF-8?q?=E7=B4=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MiniWord/MiniWord.Implment.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs index 75e540d..a1085ab 100644 --- a/src/MiniWord/MiniWord.Implment.cs +++ b/src/MiniWord/MiniWord.Implment.cs @@ -666,7 +666,8 @@ private static void ReplaceForeachStatements(OpenXmlElement xmlElement,Wordproce var copyLoopEles = betweenEles.Select(e => e.CloneNode(true)).ToList(); // 需要循环的数据 var foreachList = GetObjVal(data, foreachDataKey); - if (foreachList is IList list) + + if (foreachList is IList list && list.Count > 0) { var loopEles = new List(); for (var i = 0; i < list.Count; i++) @@ -703,6 +704,14 @@ private static void ReplaceForeachStatements(OpenXmlElement xmlElement,Wordproce } } } + else + { + // 如果没有数据,删除循环元素 + foreach (var ele in betweenEles) + { + ele.Remove(); + } + } betweenEles = GetBetweenElements(xmlElement, beginKey, endKey, false); }