diff --git a/README.zh-CN.md b/README.zh-CN.md index 4518043..22f68ec 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -184,6 +184,122 @@ MiniWord.SaveAsByTemplate(path, templatePath, value); ![image](https://user-images.githubusercontent.com/12729184/190843663-c00baf16-21f2-4579-9d08-996a2c8c549b.png) +### 二级列表 + +Tag 是 `IEnumerable` 类别. 使用方式`{{foreach` 和 `endforeach}}`. + +##### Example + +```csharp +var value = new Dictionary() +{ + ["TripHs"] = new List> + { + new Dictionary + { + { "sDate", DateTime.Parse("2022-09-08 08:30:00") }, + { "eDate", DateTime.Parse("2022-09-08 15:00:00") }, + { "How", "Discussion requirement part1" }, + { + "Details", new List() + { + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Air"}, + {"Value", "Airplane"} + }, + Separator = " | " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Parking"}, + {"Value", "Car"} + }, + Separator = " / " + } + } + } + } + } +}; +MiniWord.SaveAsByTemplate(path, templatePath, value); +``` + +##### Template + +![before_foreach](https://user-images.githubusercontent.com/38832863/220123955-063c9345-3998-4fd7-982c-8d1e3b48bbf8.PNG) + +##### Result + +![after_foreach](https://user-images.githubusercontent.com/38832863/220123960-913a7140-2fa2-415e-bb3e-456e04167382.PNG) + +### 条件判断 + + `@if` 和 `@endif` tags . + +##### Example + +```csharp +var value = new Dictionary() +{ + ["Name"] = new List(){ + new MiniWordHyperLink(){ + Url = "https://google.com", + Text = "測試連結22!!" + }, + new MiniWordHyperLink(){ + Url = "https://google1.com", + Text = "測試連結11!!" + } + }, + ["Company_Name"] = "MiniSofteware", + ["CreateDate"] = new DateTime(2021, 01, 01), + ["VIP"] = true, + ["Points"] = 123, + ["APP"] = "Demo APP", +}; +MiniWord.SaveAsByTemplate(path, templatePath, value); +``` + +##### Template + +![before_if](https://user-images.githubusercontent.com/38832863/220125429-7dd6ce94-35c6-478e-8903-064f9cf9361a.PNG) + +##### Result + +![after_if](https://user-images.githubusercontent.com/38832863/220125435-72ea24b4-2412-45de-961a-ad4b2134417b.PNG) + +### 多彩字体 + +##### 代码例子 + +```csharp +var value = new +{ + Company_Name = new MiniWordColorText { Text = "MiniSofteware", FontColor = "#eb70AB", }, + Name = new[] { + new MiniWordColorText { Text = "Ja", HighlightColor = "#eb70AB" }, + new MiniWordColorText { Text = "ck", HighlightColor = "#a56abe" } + }, + CreateDate = new MiniWordColorText + { + Text = new DateTime(2021, 01, 01).ToString(), + HighlightColor = "#eb70AB", + FontColor = "#ffffff", + }, + VIP = true, + Points = 123, + APP = "Demo APP", +}; +MiniWord.SaveAsByTemplate(path, templatePath, value); +``` + + + ## 其他 diff --git a/README.zh-Hant.md b/README.zh-Hant.md index 9d8a6c6..5c8afb8 100644 --- a/README.zh-Hant.md +++ b/README.zh-Hant.md @@ -184,6 +184,122 @@ MiniWord.SaveAsByTemplate(path, templatePath, value); +### 二级列表 + +Tag 是 `IEnumerable` 类别. 使用方式`{{foreach` 和 `endforeach}}`. + +##### Example + +```csharp +var value = new Dictionary() +{ + ["TripHs"] = new List> + { + new Dictionary + { + { "sDate", DateTime.Parse("2022-09-08 08:30:00") }, + { "eDate", DateTime.Parse("2022-09-08 15:00:00") }, + { "How", "Discussion requirement part1" }, + { + "Details", new List() + { + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Air"}, + {"Value", "Airplane"} + }, + Separator = " | " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Parking"}, + {"Value", "Car"} + }, + Separator = " / " + } + } + } + } + } +}; +MiniWord.SaveAsByTemplate(path, templatePath, value); +``` + +##### Template + +![before_foreach](https://user-images.githubusercontent.com/38832863/220123955-063c9345-3998-4fd7-982c-8d1e3b48bbf8.PNG) + +##### Result + +![after_foreach](https://user-images.githubusercontent.com/38832863/220123960-913a7140-2fa2-415e-bb3e-456e04167382.PNG) + +### 条件判断 + + `@if` 和 `@endif` tags . + +##### Example + +```csharp +var value = new Dictionary() +{ + ["Name"] = new List(){ + new MiniWordHyperLink(){ + Url = "https://google.com", + Text = "測試連結22!!" + }, + new MiniWordHyperLink(){ + Url = "https://google1.com", + Text = "測試連結11!!" + } + }, + ["Company_Name"] = "MiniSofteware", + ["CreateDate"] = new DateTime(2021, 01, 01), + ["VIP"] = true, + ["Points"] = 123, + ["APP"] = "Demo APP", +}; +MiniWord.SaveAsByTemplate(path, templatePath, value); +``` + +##### Template + +![before_if](https://user-images.githubusercontent.com/38832863/220125429-7dd6ce94-35c6-478e-8903-064f9cf9361a.PNG) + +##### Result + +![after_if](https://user-images.githubusercontent.com/38832863/220125435-72ea24b4-2412-45de-961a-ad4b2134417b.PNG) + +### 多彩字體 + +##### 例子 + +```csharp +var value = new +{ + Company_Name = new MiniWordColorText { Text = "MiniSofteware", FontColor = "#eb70AB", }, + Name = new[] { + new MiniWordColorText { Text = "Ja", HighlightColor = "#eb70AB" }, + new MiniWordColorText { Text = "ck", HighlightColor = "#a56abe" } + }, + CreateDate = new MiniWordColorText + { + Text = new DateTime(2021, 01, 01).ToString(), + HighlightColor = "#eb70AB", + FontColor = "#ffffff", + }, + VIP = true, + Points = 123, + APP = "Demo APP", +}; +MiniWord.SaveAsByTemplate(path, templatePath, value); +``` + + + ## 其他 ### POCO or dynamic 參數 diff --git a/release-note/README.md b/release-note/README.md index 52326e8..d5f39fc 100644 --- a/release-note/README.md +++ b/release-note/README.md @@ -22,10 +22,18 @@ --- +### 0.8.0 -### 0.6.2 +- [New] Support new OpenXml to solve the problem of line wrapping to multiple lines #68 (via @ping9719) + +- [New] Support if statement inside foreach statement inside templates. Please refer to samples. (via @eynarhaji) +- [New] Change tags for if statements for single paragraph if statement {{if and endif}} inside templates. Please refer to samples. (via @eynarhaji) +- [Bug] The table should be inserted at the template tag position instead of the last row #47 (via @itldg) + +### 0.7.0 - [New] Add support to List inside List via `IEnumerable` and `{{foreach`/`endforeach}}` tags (via @eynarhaji) - [New] Add support to @if statements inside templates (via @eynarhaji) +- [New] Support multiple color word by word (via @andy505050) ### 0.6.1 - [Bug] Fixed system does not support `IEnumerable` (#39 via @isdaniel) diff --git a/release-note/README.zh-CN.md b/release-note/README.zh-CN.md index f1bc952..e45d4d8 100644 --- a/release-note/README.zh-CN.md +++ b/release-note/README.zh-CN.md @@ -22,6 +22,18 @@ --- +### 0.8.0 + +- [New] 支持 new OpenXml to solve the problem of line wrapping to multiple lines #68 (via @ping9719) +- [New] 支持 to if statement inside foreach statement inside templates. Please refer to samples. (via @eynarhaji) +- [New] 变更 tags for if statements for single paragraph if statement {{if and endif}} inside templates. Please refer to samples. (via @eynarhaji) +- [Bug] The table should be inserted at the template tag position instead of the last row #47 (via @itldg) + +### 0.7.0 +- [New] 支持 List inside List via `IEnumerable` and `{{foreach`/`endforeach}}` tags (via @eynarhaji) +- [New] 支持 @if statements inside templates (via @eynarhaji) +- [New] 支持 multiple color word by word (via @andy505050) + ### 0.6.1 - [Bug] 修正系统不支持 `IEnumerable` (#39 via @isdaniel) diff --git a/release-note/README.zh-Hant.md b/release-note/README.zh-Hant.md index 292fa77..effddd2 100644 --- a/release-note/README.zh-Hant.md +++ b/release-note/README.zh-Hant.md @@ -22,6 +22,18 @@ --- +### 0.8.0 + +- [New] 支持 new OpenXml to solve the problem of line wrapping to multiple lines #68 (via @ping9719) +- [New] 支持 to if statement inside foreach statement inside templates. Please refer to samples. (via @eynarhaji) +- [New] 变更 tags for if statements for single paragraph if statement {{if and endif}} inside templates. Please refer to samples. (via @eynarhaji) +- [Bug] The table should be inserted at the template tag position instead of the last row #47 (via @itldg) + +### 0.7.0 +- [New] 支持 List inside List via `IEnumerable` and `{{foreach`/`endforeach}}` tags (via @eynarhaji) +- [New] 支持 @if statements inside templates (via @eynarhaji) +- [New] 支持 multiple color word by word (via @andy505050) + ### 0.6.1 - [Bug] 修正系統不支持 `IEnumerable` (#39 via @isdaniel) diff --git a/samples/docx/TestForeachInTablesWithIfStatementDemo.docx b/samples/docx/TestForeachInTablesWithIfStatementDemo.docx new file mode 100644 index 0000000..168c5cb Binary files /dev/null and b/samples/docx/TestForeachInTablesWithIfStatementDemo.docx differ diff --git a/samples/docx/TestIfStatement.docx b/samples/docx/TestIfStatement.docx index 8e39d67..23fab8a 100644 Binary files a/samples/docx/TestIfStatement.docx and b/samples/docx/TestIfStatement.docx differ diff --git a/samples/docx/TestIssue47.docx b/samples/docx/TestIssue47.docx new file mode 100644 index 0000000..ec5b5e2 Binary files /dev/null and b/samples/docx/TestIssue47.docx differ diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs index e6bf400..3234eba 100644 --- a/src/MiniWord/MiniWord.Implment.cs +++ b/src/MiniWord/MiniWord.Implment.cs @@ -1,14 +1,13 @@ -namespace MiniSoftware +namespace MiniSoftware { using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; - using MiniSoftware.Extensions; - using MiniSoftware.Utility; + using Extensions; + using Utility; using System; using System.Collections; using System.Collections.Generic; - using System.Dynamic; using System.IO; using System.Linq; using System.Text; @@ -29,10 +28,12 @@ private static void SaveAsByTemplateImpl(Stream stream, byte[] template, Diction ms.Position = 0; using (var docx = WordprocessingDocument.Open(ms, true)) { - foreach (var hdr in docx.MainDocumentPart.HeaderParts) - hdr.Header.Generate(docx, value); - foreach (var ftr in docx.MainDocumentPart.FooterParts) - ftr.Footer.Generate(docx, value); + var hc = docx.MainDocumentPart.HeaderParts.Count(); + var fc = docx.MainDocumentPart.FooterParts.Count(); + for (int i = 0; i < hc; i++) + docx.MainDocumentPart.HeaderParts.ElementAt(i).Header.Generate(docx, value); + for (int i = 0; i < fc; i++) + docx.MainDocumentPart.FooterParts.ElementAt(i).Footer.Generate(docx, value); docx.MainDocumentPart.Document.Body.Generate(docx, value); docx.Save(); } @@ -56,7 +57,9 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum foreach (var tr in trs) { - var matchs = (Regex.Matches(tr.InnerText, "(?<={{).*?\\..*?(?=}})") + 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) { @@ -82,10 +85,19 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum dic.Add(dicKey, e.Value); } - ReplaceStatements(xmlElement, tags); + ReplaceStatements(newTr, tags: dic); ReplaceText(newTr, docx, tags: dic); - table.Append(newTr); + //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(); } @@ -95,7 +107,7 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum } ReplaceStatements(xmlElement, tags); - + ReplaceText(xmlElement, docx, tags); } @@ -105,7 +117,6 @@ private static void AvoidSplitTagText(OpenXmlElement xmlElement) var pool = new List(); var sb = new StringBuilder(); var needAppend = false; - var foreachIncluded = false; foreach (var text in texts) { var clear = false; @@ -122,28 +133,21 @@ private static void AvoidSplitTagText(OpenXmlElement xmlElement) // TODO: check tag exist // TODO: record tag text if without tag then system need to clear them // TODO: every {{tag}} one for them and add text before first text and copy first one and remove {{, tagname, }} - - if(s.StartsWith("{{foreach")) - foreachIncluded = true; - - if (!s.StartsWith("{{")) - clear = true; - else if (s.Contains("{{") && s.Contains("}}") && !foreachIncluded) - { - if (sb.Length <= 1000) // avoid too big tag - { - var first = pool.First(); - var newText = first.Clone() as Text; - newText.Text = s; - first.Parent.InsertBefore(newText, first); - foreach (var t in pool) - { - t.Text = ""; - } - } - clear = true; - } - else if (s.Contains("{{foreach") && s.Contains("endforeach}}") && foreachIncluded) + + const string foreachTag = "{{foreach"; + const string endForeachTag = "endforeach}}"; + const string ifTag = "{{if"; + const string endifTag = "endif}}"; + const string tagStart = "{{"; + const string tagEnd = "}}"; + + var foreachTagContains = s.Split(new []{foreachTag}, StringSplitOptions.None).Length - 1 == + s.Split(new []{endForeachTag}, StringSplitOptions.None).Length - 1; + var ifTagContains = s.Split(new []{ifTag}, StringSplitOptions.None).Length - 1 == + s.Split(new []{endifTag}, StringSplitOptions.None).Length - 1; + var tagContains = s.StartsWith(tagStart) && s.Contains(tagEnd); + + if (foreachTagContains && ifTagContains && tagContains) { if (sb.Length <= 1000) // avoid too big tag { @@ -157,9 +161,7 @@ private static void AvoidSplitTagText(OpenXmlElement xmlElement) } } clear = true; - foreachIncluded = false; } - } if (clear) @@ -212,60 +214,34 @@ private static List GetReplaceKeys(Dictionary tags) return keys; } - private static void ReplaceStatements(OpenXmlElement xmlElement, Dictionary tags) - { - var paragraphs = xmlElement.Descendants().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 statement = paragraphs[ifIndex].InnerText.Split(' '); - - var checkStatement = EvaluateStatement(tags[statement[1]], statement[2], statement[3]); - - if (checkStatement) - { - for (int i = ifIndex+1; i <= endIfFinalIndex-1; i++) - { - paragraphs[i].Remove(); - } - } - - paragraphs[ifIndex].Remove(); - paragraphs[endIfFinalIndex].Remove(); - - paragraphs = xmlElement.Descendants().ToList(); - } - } - - private static bool EvaluateStatement(object tagValue, string comparisonOperator, string value) + private static bool EvaluateStatement(string tagValue, string comparisonOperator, string value) { var checkStatement = false; + + var tagValueEvaluation = EvaluateValue(tagValue); - switch (tagValue) + switch (tagValueEvaluation) { case double dtg when double.TryParse(value, out var doubleNumber): switch (comparisonOperator) { case "==": - checkStatement = !dtg.Equals(doubleNumber); + checkStatement = dtg.Equals(doubleNumber); break; case "!=": - checkStatement = dtg.Equals(doubleNumber); + checkStatement = !dtg.Equals(doubleNumber); break; case ">": - checkStatement = dtg <= doubleNumber; + checkStatement = dtg > doubleNumber; break; case "<": - checkStatement = dtg >= doubleNumber; + checkStatement = dtg < doubleNumber; break; case ">=": - checkStatement = dtg < doubleNumber; + checkStatement = dtg >= doubleNumber; break; case "<=": - checkStatement = dtg > doubleNumber; + checkStatement = dtg <= doubleNumber; break; } @@ -274,22 +250,22 @@ private static bool EvaluateStatement(object tagValue, string comparisonOperator switch (comparisonOperator) { case "==": - checkStatement = !itg.Equals(intNumber); + checkStatement = itg.Equals(intNumber); break; case "!=": - checkStatement = itg.Equals(intNumber); + checkStatement = !itg.Equals(intNumber); break; case ">": - checkStatement = itg <= intNumber; + checkStatement = itg > intNumber; break; case "<": - checkStatement = itg >= intNumber; + checkStatement = itg < intNumber; break; case ">=": - checkStatement = itg < intNumber; + checkStatement = itg >= intNumber; break; case "<=": - checkStatement = itg > intNumber; + checkStatement = itg <= intNumber; break; } @@ -298,22 +274,22 @@ private static bool EvaluateStatement(object tagValue, string comparisonOperator switch (comparisonOperator) { case "==": - checkStatement = !dttg.Equals(date); + checkStatement = dttg.Equals(date); break; case "!=": - checkStatement = dttg.Equals(date); + checkStatement = !dttg.Equals(date); break; case ">": - checkStatement = dttg <= date; + checkStatement = dttg > date; break; case "<": - checkStatement = dttg >= date; + checkStatement = dttg < date; break; case ">=": - checkStatement = dttg < date; + checkStatement = dttg >= date; break; case "<=": - checkStatement = dttg > date; + checkStatement = dttg <= date; break; } @@ -322,19 +298,42 @@ private static bool EvaluateStatement(object tagValue, string comparisonOperator switch (comparisonOperator) { case "==": - checkStatement = stg != value; + checkStatement = stg == value; break; case "!=": - checkStatement = stg == value; + checkStatement = stg != value; break; } + break; + case bool btg when bool.TryParse(value, out var boolean): + switch (comparisonOperator) + { + case "==": + checkStatement = btg != boolean; + break; + case "!=": + checkStatement = btg == boolean; + break; + } - break; - } + break; + } return checkStatement; } + private static object EvaluateValue(string value) + { + if (double.TryParse(value, out var doubleNumber)) + return doubleNumber; + else if (int.TryParse(value, out var intNumber)) + return intNumber; + else if (DateTime.TryParse(value, out var date)) + return date; + + return value; + } + private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocument docx, Dictionary tags) { var paragraphs = xmlElement.Descendants().ToArray(); @@ -364,7 +363,7 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen isMatch = true; } } - + if (isMatch) { if (tag.Value is string[] || tag.Value is IList || tag.Value is List) @@ -380,6 +379,7 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen isFirst = false; else run.Append(new Break()); + newT.Text = EvaluateIfStatement(newT.Text); run.Append(newT); currentT = newT; } @@ -390,6 +390,8 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen var currentT = t; var generatedText = new Text(); currentT.Text = currentT.Text.Replace(@"{{foreach", "").Replace(@"endforeach}}", ""); + + var newTexts = new Dictionary(); for (var i = 0; i < vs.Count; i++) { var newT = t.CloneNode(true) as Text; @@ -398,13 +400,22 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen { newT.Text = newT.Text.Replace("{{" + tag.Key + "." + vv.Key + "}}", vv.Value.ToString()); } + + newT.Text = EvaluateIfStatement(newT.Text); + + if(!string.IsNullOrEmpty(newT.Text)) + newTexts.Add(i, newT.Text); + } - if (i != vs.Count) + for (var i = 0; i < newTexts.Count; i++) + { + var dict = newTexts.ElementAt(i); + generatedText.Text += dict.Value; + + if (i != newTexts.Count - 1) { - newT.Text += vs[i].Separator; + generatedText.Text += vs[dict.Key].Separator; } - - generatedText.Text += newT.Text; } run.Append(generatedText); @@ -458,18 +469,21 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen { newText = tag.Value?.ToString(); } + t.Text = t.Text.Replace($"{{{{{tag.Key}}}}}", newText); } } } + t.Text = EvaluateIfStatement(t.Text); + // add breakline { var newText = t.Text; - var splits = Regex.Split(newText, "(<[a-zA-Z/].*?>|\n)"); + 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.Length > 1) + if (splits.Count() > 1) { foreach (var v in splits) { @@ -489,6 +503,67 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen } } } + + private static void ReplaceStatements(OpenXmlElement xmlElement, Dictionary tags) + { + var paragraphs = xmlElement.Descendants().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 statement = paragraphs[ifIndex].InnerText.Split(' '); + + var tagValue = tags[statement[1]] ?? "NULL"; + + var checkStatement = statement.Length == 4 ? EvaluateStatement(tagValue.ToString(), statement[2], statement[3]) : !bool.Parse(tagValue.ToString()); + + if (!checkStatement) + { + for (int i = ifIndex + 1; i <= endIfFinalIndex - 1; i++) + { + paragraphs[i].Remove(); + } + } + + paragraphs[ifIndex].Remove(); + paragraphs[endIfFinalIndex].Remove(); + + paragraphs = xmlElement.Descendants().ToList(); + } + } + + private static string EvaluateIfStatement(string text) + { + const string ifStartTag = "{{if("; + const string ifEndTag = ")if"; + const string endIfTag = "endif}}"; + + while (text.Contains(ifStartTag)) + { + var ifIndex = text.IndexOf(ifStartTag, StringComparison.Ordinal); + var ifEndIndex = text.IndexOf(")if", ifIndex, StringComparison.Ordinal); + + var statement = text.Substring(ifIndex + ifStartTag.Length, ifEndIndex - (ifIndex + ifStartTag.Length)).Split(','); + + var checkStatement = EvaluateStatement(statement[0], statement[1], statement[2]); + + if (checkStatement) + { + text = text.Remove(ifIndex, ifEndIndex - ifIndex + ifEndTag.Length); + var endIfFinalIndex = text.IndexOf(endIfTag, StringComparison.Ordinal); + text = text.Remove(endIfFinalIndex, endIfTag.Length); + } + else + { + var endIfFinalIndex = text.IndexOf(endIfTag, StringComparison.Ordinal); + text = text.Remove(ifIndex, endIfFinalIndex - ifIndex + endIfTag.Length); + } + } + + return text; + } private static bool IsHyperLink(object value) { @@ -536,7 +611,6 @@ private static Hyperlink GetHyperLink(MainDocumentPart mainPart, MiniWordHyperLi } private static RunProperties AddColorText(MiniWordColorText[] miniWordColorTextArray) { - RunProperties runPro = new RunProperties(); foreach (var miniWordColorText in miniWordColorTextArray) { @@ -587,7 +661,7 @@ private static void AddPicture(OpenXmlElement appendElement, string relationship new A.BlipExtension() { Uri = - $"{{{ Guid.NewGuid().ToString("n")}}}" + $"{{{Guid.NewGuid().ToString("n")}}}" }) ) { diff --git a/src/MiniWord/MiniWord.csproj b/src/MiniWord/MiniWord.csproj index 7a86222..53ce9fc 100644 --- a/src/MiniWord/MiniWord.csproj +++ b/src/MiniWord/MiniWord.csproj @@ -1,7 +1,7 @@  - net45;netstandard2.0;net5.0 - 0.6.2 + net45;netstandard2.0; + 0.8.0 MiniWord @@ -10,9 +10,9 @@ word;template;micro-helper;mini;openxml;helper; .NET Word(docx) exporting template engine without COM+ and interop (support Linux and Mac) - LIN,WEI-HAN, eynarhaji + mini-software, Wei Lin, eynarhaji MiniWord - LIN,WEI-HAN, 2022 onwards + mini-software, Wei Lin, 2022 onwards en https://raw.githubusercontent.com/mini-software/MiniWord/main/LICENSE MiniSoftware @@ -32,6 +32,6 @@ - + diff --git a/src/MiniWord/MiniWordPicture.cs b/src/MiniWord/MiniWordPicture.cs index 38f8b94..8dab97c 100644 --- a/src/MiniWord/MiniWordPicture.cs +++ b/src/MiniWord/MiniWordPicture.cs @@ -24,7 +24,7 @@ public string Extension } set { _extension = value; } } - internal ImagePartType GetImagePartType + internal PartTypeInfo GetImagePartType { get { diff --git a/tests/MiniWordTests/IssueTests.cs b/tests/MiniWordTests/IssueTests.cs index 4223536..64f0935 100644 --- a/tests/MiniWordTests/IssueTests.cs +++ b/tests/MiniWordTests/IssueTests.cs @@ -9,6 +9,46 @@ namespace MiniWordTests { public class IssueTests { + [Fact] + public void TestIssue69() + { + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestIssue47.docx"); + var value = new Dictionary() + { + { "line",new List> + { + new Dictionary + { + {"num", "1"}, + {"spc", "Specification1"}, + {"desc", "Description1"}, + {"qty", "10"}, + {"up", "20.00"}, + {"tax_rate", "0.20"}, + {"tax_amount", "4.00"} + }, + new Dictionary + { + {"num", "2"}, + {"spc", "Specification2"}, + {"desc", "Description2"}, + {"qty", "5"}, + {"up", "30.00"}, + {"tax_rate", "0.15"}, + {"tax_amount", "2.25"} + } + } + } + }; + MiniWord.SaveAsByTemplate(path, templatePath, value); + // TODO: waiting solution + //var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + //Assert.Contains(@"MiniWord", xml); + } + } + [Fact] public void TestIssueAvoidSplitTag() { @@ -149,7 +189,7 @@ public void TestIssue18() new User (){ name="Jack",department="HR"}, new User (){ name="Loan",department="IT"}, }, - employees = new List() + employees = new List() { new User (){ name="Jack",department="HR"}, new User (){ name="Loan",department="HR"}, @@ -193,7 +233,7 @@ public void TestIssue17_new() { var path = PathHelper.GetTempFilePath(); var templatePath = PathHelper.GetFile("TestIssue17.docx"); - var value = new + var value = new { Content = "Test", Content2 = "Test2", @@ -250,7 +290,7 @@ public void TestIssue13_new() { var path = PathHelper.GetTempFilePath(); var templatePath = PathHelper.GetFile("TestExpenseDemo.docx"); - var value = new + var value = new { TripHs = new List> { @@ -343,7 +383,7 @@ public void TestIssue12() [Fact] public void TestIssue12_dynamic() { - + var path = PathHelper.GetTempFilePath(); var templatePath = PathHelper.GetFile("TestBasicFill.docx"); dynamic value = new ExpandoObject(); @@ -353,7 +393,7 @@ public void TestIssue12_dynamic() value.VIP = true; value.Points = 123; value.APP = "Demo APP\n"; - + MiniWord.SaveAsByTemplate(path, templatePath, value); var xml = Helpers.GetZipFileContent(path, "word/document.xml"); Assert.Contains(@"MiniSofteware", xml); @@ -369,7 +409,8 @@ public void TestIssue12_new() var path = PathHelper.GetTempFilePath(); var templatePath = PathHelper.GetFile("TestBasicFill.docx"); - object value = new { + object value = new + { Company_Name = "MiniSofteware\n", Name = "Jack", CreateDate = new DateTime(2021, 01, 01), @@ -434,7 +475,7 @@ Lorem Ipsum has been the industry's standard dummy text [Fact] public void TestIssueDemo03_dynamic() { - + var path = PathHelper.GetTempFilePath(); var templatePath = PathHelper.GetFile("TestDemo02.docx"); dynamic value = new ExpandoObject(); @@ -571,7 +612,8 @@ public void TestIssue5() var templatePath = PathHelper.GetFile("TestBasicFill.docx"); var value = new Dictionary() { - ["Name"] = new MiniWordHyperLink(){ + ["Name"] = new MiniWordHyperLink() + { Url = "https://google.com", Text = "測試連結!!" }, @@ -669,7 +711,7 @@ public void MiniWordHyperLink_AnonymousArray() var templatePath = PathHelper.GetFile("TestBasicFill.docx"); var value = new Dictionary() { - ["Name"] = new []{ + ["Name"] = new[]{ new MiniWordHyperLink(){ Url = "https://google.com", Text = "測試連結22!!" @@ -706,9 +748,10 @@ public void TestIssue5_new() { var path = PathHelper.GetTempFilePath(); var templatePath = PathHelper.GetFile("TestBasicFill.docx"); - var value = new + var value = new { - Name =new MiniWordHyperLink(){ + Name = new MiniWordHyperLink() + { Url = "https://google.com", Text = "測試連結!!" }, @@ -755,7 +798,7 @@ public void TestIssue4_new() { var path = PathHelper.GetTempFilePath(); var templatePath = PathHelper.GetFile("TestBasicFill.docx"); - var value = new + var value = new { Company_Name = "MiniSofteware", Name = "Jack", @@ -775,8 +818,13 @@ public void TestColor() { Company_Name = new MiniWordColorText { Text = "MiniSofteware", FontColor = "#eb70AB" }, Name = new MiniWordColorText { Text = "Jack", HighlightColor = "#eb70AB" }, - CreateDate = new MiniWordColorText { Text = new DateTime(2021, 01, 01).ToString() - , HighlightColor = "#eb70AB", FontColor = "#ffffff" }, + CreateDate = new MiniWordColorText + { + Text = new DateTime(2021, 01, 01).ToString() + , + HighlightColor = "#eb70AB", + FontColor = "#ffffff" + }, VIP = true, Points = 123, APP = "Demo APP", diff --git a/tests/MiniWordTests/MiniWordTests.cs b/tests/MiniWordTests/MiniWordTests.cs index fa58d7a..78cf72d 100644 --- a/tests/MiniWordTests/MiniWordTests.cs +++ b/tests/MiniWordTests/MiniWordTests.cs @@ -118,6 +118,8 @@ public void TestForeachLoopInTables() var xml = Helpers.GetZipFileContent(path, "word/document.xml"); Assert.Contains(@"Discussion requirement part2 and development", xml); Assert.Contains(@"Discussion requirement part1", xml); + Assert.Contains( + "Air way to the Airplane | Parking way to the Car / Hotel way to the Room, Food way to the Plate", xml); } [Fact] @@ -152,5 +154,118 @@ public void MiniWordIfStatement_FirstIf() Assert.Contains("CreateDate is not less than 2021", docXml); Assert.DoesNotContain("CreateDate is not greater than 2021", docXml); } + + [Fact] + public void TestForeachLoopInTablesWithIfStatement() + { + var path = PathHelper.GetTempFilePath(); + var templatePath = PathHelper.GetFile("TestForeachInTablesWithIfStatementDemo.docx"); + var value = new Dictionary() + { + ["TripHs"] = new List> + { + new Dictionary + { + { "sDate", DateTime.Parse("2022-09-08 08:30:00") }, + { "eDate", DateTime.Parse("2022-09-08 15:00:00") }, + { "How", "Discussion requirement part1" }, + { + "Details", new List() + { + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Air"}, + {"Value", "Airplane"} + }, + Separator = " | " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Parking"}, + {"Value", "Car"} + }, + Separator = " / " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Hotel"}, + {"Value", "Room"} + }, + Separator = ", " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Food"}, + {"Value", "Plate"} + }, + Separator = "" + } + } + } + }, + new Dictionary + { + { "sDate", DateTime.Parse("2022-09-09 08:30:00") }, + { "eDate", DateTime.Parse("2022-09-09 17:00:00") }, + { "How", "Discussion requirement part2 and development" }, + { + "Details", new List() + { + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Air"}, + {"Value", "Airplane"} + }, + Separator = " | " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Parking"}, + {"Value", "Car"} + }, + Separator = " / " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Hotel"}, + {"Value", "Room"} + }, + Separator = ", " + }, + new MiniWordForeach() + { + Value = new Dictionary() + { + {"Text", "Food"}, + {"Value", "Plate"} + }, + Separator = "" + } + } + } + } + } + }; + MiniWord.SaveAsByTemplate(path, templatePath, value); + //System.Diagnostics.Process.Start("explorer.exe", path); + var xml = Helpers.GetZipFileContent(path, "word/document.xml"); + Assert.Contains(@"Discussion requirement part2 and development", xml); + Assert.Contains(@"Discussion requirement part1", xml); + Assert.Contains("Air way to the Airplane | Hotel way to the Room", xml); + } } } \ No newline at end of file diff --git a/tests/MiniWordTests/MiniWordTests.csproj b/tests/MiniWordTests/MiniWordTests.csproj index 69fdce8..953e880 100644 --- a/tests/MiniWordTests/MiniWordTests.csproj +++ b/tests/MiniWordTests/MiniWordTests.csproj @@ -1,7 +1,7 @@ - net5.0 + net5.0; false