diff --git a/src/MiniWord/MiniWord.Implment.cs b/src/MiniWord/MiniWord.Implment.cs index 7009950..756a0cc 100644 --- a/src/MiniWord/MiniWord.Implment.cs +++ b/src/MiniWord/MiniWord.Implment.cs @@ -15,8 +15,6 @@ namespace MiniSoftware using A = DocumentFormat.OpenXml.Drawing; using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing; using PIC = DocumentFormat.OpenXml.Drawing.Pictures; - using System.Threading.Tasks; - using System.Threading; public static partial class MiniWord { @@ -27,39 +25,22 @@ private static void SaveAsByTemplateImpl(Stream stream, byte[] template, Diction using (var ms = new MemoryStream()) { ms.Write(template, 0, template.Length); - bytes = WriteToByte(data, ms); + ms.Position = 0; + using (var docx = WordprocessingDocument.Open(ms, true)) + { + 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(); + } + bytes = ms.ToArray(); } stream.Write(bytes, 0, bytes.Length); } - - private static async Task SaveAsByTemplateImplAsync(Stream stream, byte[] template, Dictionary data,CancellationToken token) - { - byte[] bytes = null; - using (var ms = new MemoryStream()) - { - await ms.WriteAsync(template, 0, template.Length, token); - bytes = WriteToByte(data, ms); - } - await stream.WriteAsync(bytes, 0, bytes.Length,token); - } - - private static byte[] WriteToByte(Dictionary value, MemoryStream ms) - { - ms.Position = 0; - using (var docx = WordprocessingDocument.Open(ms, true)) - { - 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(); - } - return ms.ToArray(); - } - private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocument docx, Dictionary tags) { // avoid {{tag}} like {{ @@ -72,14 +53,14 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum { foreach (var table in tables) { - var trs = table.Descendants().ToArray(); // remember toarray otherwise system will loop OOM; + var trs = table.Descendants().ToArray(); // remember toarray or system will loop OOM; 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(); + 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(); @@ -95,10 +76,17 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum foreach (Dictionary es in list) { - var dic = es.ToDictionary(e => $"{listKey}.{e.Key}", e => e.Value); + var dic = new Dictionary(); //TODO: optimize + var newTr = tr.CloneNode(true); + foreach (var e in es) + { + var dicKey = $"{listKey}.{e.Key}"; + dic.Add(dicKey, e.Value); + } + ReplaceStatements(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)) @@ -117,9 +105,9 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum } } } - + ReplaceStatements(xmlElement, tags); - + ReplaceText(xmlElement, docx, tags); } @@ -145,20 +133,20 @@ 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, }} - + 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 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 @@ -231,91 +219,91 @@ private static bool EvaluateStatement(string tagValue, string comparisonOperator var checkStatement = false; var tagValueEvaluation = EvaluateValue(tagValue); - + switch (tagValueEvaluation) - { - case double dtg when double.TryParse(value, out var doubleNumber): - switch (comparisonOperator) - { - case "==": - checkStatement = dtg.Equals(doubleNumber); - break; - case "!=": - checkStatement = !dtg.Equals(doubleNumber); - break; - case ">": - checkStatement = dtg > doubleNumber; - break; - case "<": - checkStatement = dtg < doubleNumber; - break; - case ">=": - checkStatement = dtg >= doubleNumber; - break; - case "<=": - checkStatement = dtg <= doubleNumber; - break; - } + { + case double dtg when double.TryParse(value, out var doubleNumber): + switch (comparisonOperator) + { + case "==": + checkStatement = dtg.Equals(doubleNumber); + break; + case "!=": + checkStatement = !dtg.Equals(doubleNumber); + break; + case ">": + checkStatement = dtg > doubleNumber; + break; + case "<": + checkStatement = dtg < doubleNumber; + break; + case ">=": + checkStatement = dtg >= doubleNumber; + break; + case "<=": + checkStatement = dtg <= doubleNumber; + break; + } - break; - case int itg when int.TryParse(value, out var intNumber): - switch (comparisonOperator) - { - case "==": - checkStatement = itg.Equals(intNumber); - break; - case "!=": - checkStatement = !itg.Equals(intNumber); - break; - case ">": - checkStatement = itg > intNumber; - break; - case "<": - checkStatement = itg < intNumber; - break; - case ">=": - checkStatement = itg >= intNumber; - break; - case "<=": - checkStatement = itg <= intNumber; - break; - } + break; + case int itg when int.TryParse(value, out var intNumber): + switch (comparisonOperator) + { + case "==": + checkStatement = itg.Equals(intNumber); + break; + case "!=": + checkStatement = !itg.Equals(intNumber); + break; + case ">": + checkStatement = itg > intNumber; + break; + case "<": + checkStatement = itg < intNumber; + break; + case ">=": + checkStatement = itg >= intNumber; + break; + case "<=": + checkStatement = itg <= intNumber; + break; + } - break; - case DateTime dttg when DateTime.TryParse(value, out var date): - switch (comparisonOperator) - { - case "==": - checkStatement = dttg.Equals(date); - break; - case "!=": - checkStatement = !dttg.Equals(date); - break; - case ">": - checkStatement = dttg > date; - break; - case "<": - checkStatement = dttg < date; - break; - case ">=": - checkStatement = dttg >= date; - break; - case "<=": - checkStatement = dttg <= date; - break; - } + break; + case DateTime dttg when DateTime.TryParse(value, out var date): + switch (comparisonOperator) + { + case "==": + checkStatement = dttg.Equals(date); + break; + case "!=": + checkStatement = !dttg.Equals(date); + break; + case ">": + checkStatement = dttg > date; + break; + case "<": + checkStatement = dttg < date; + break; + case ">=": + checkStatement = dttg >= date; + break; + case "<=": + checkStatement = dttg <= date; + break; + } - break; - case string stg: - switch (comparisonOperator) - { - case "==": - checkStatement = stg == value; - break; - case "!=": - checkStatement = stg != value; - break; - } + break; + case string stg: + switch (comparisonOperator) + { + case "==": + checkStatement = stg == value; + break; + case "!=": + checkStatement = stg != value; + break; + } break; case bool btg when bool.TryParse(value, out var boolean): switch (comparisonOperator) @@ -342,7 +330,7 @@ private static object EvaluateValue(string value) return intNumber; else if (DateTime.TryParse(value, out var date)) return date; - + return value; } @@ -367,9 +355,10 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen if (!isMatch && tag.Value is List forTags) { if (forTags.Any(forTag => forTag.Value.Keys.Any(dictKey => - { - return t.Text.Contains($@"{{{{{tag.Key}.{dictKey}}}}}"); - }))) + { + var innerTag = "{{" + tag.Key + "." + dictKey + "}}"; + return t.Text.Contains(innerTag); + }))) { isMatch = true; } @@ -401,7 +390,7 @@ 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++) { @@ -411,10 +400,10 @@ 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)) + + if (!string.IsNullOrEmpty(newT.Text)) newTexts.Add(i, newT.Text); } @@ -422,7 +411,7 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen { var dict = newTexts.ElementAt(i); generatedText.Text += dict.Value; - + if (i != newTexts.Count - 1) { generatedText.Text += vs[dict.Key].Separator; @@ -463,10 +452,14 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen } else if (tag.Value is MiniWordColorText || tag.Value is MiniWordColorText[]) { - var colorText = tag.Value is MiniWordColorText - ? AddColorText(new[] { (MiniWordColorText)tag.Value }) - : AddColorText((MiniWordColorText[])tag.Value); - run.Append(colorText); + if (tag.Value is MiniWordColorText) + { + AddColorText(run, new[] { (MiniWordColorText)tag.Value }); + } + else + { + AddColorText(run, (MiniWordColorText[])tag.Value); + } t.Remove(); } else @@ -480,7 +473,7 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen { newText = tag.Value?.ToString(); } - + t.Text = t.Text.Replace($"{{{{{tag.Key}}}}}", newText); } } @@ -514,7 +507,7 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen } } } - + private static void ReplaceStatements(OpenXmlElement xmlElement, Dictionary tags) { var paragraphs = xmlElement.Descendants().ToList(); @@ -526,7 +519,7 @@ private static void ReplaceStatements(OpenXmlElement xmlElement, Dictionary GetByteAsync(string path) - { - using (var st = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true)) - using (var ms = new MemoryStream()) - { - await st.CopyToAsync(ms); - return ms.ToArray(); - } - } } -} \ No newline at end of file +}